init
This commit is contained in:
commit
c892ebfd44
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
yarn.lock
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
97
README.md
Normal file
97
README.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
## Introduction
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://www.npmjs.com/package/@layui/layer-vue"><img src="https://img.shields.io/npm/v/@layui/layer-vue.svg?sanitize=true"></a>
|
||||||
|
<a href="https://www.npmjs.com/package/@layui/layer-vue"><img src="https://img.shields.io/npm/l/@layui/layer-vue.svg?sanitize=true"></a>
|
||||||
|
<a href="https://travis-ci.org/sentsin/layui"><img src="https://img.shields.io/travis/sentsin/layui/master.svg"></a>
|
||||||
|
<a href="https://coveralls.io/r/sentsin/layui?branch=master"><img src="https://img.shields.io/coveralls/sentsin/layui/master.svg"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
**[🔶 Explore the docs »](http://layer.layui-vue.com)** **[Join us](https://jq.qq.com/?_wv=1027&k=ffiUQgnE)**
|
||||||
|
|
||||||
|
Layer vue is a web elastic layer component.
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
Use npm to install.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i @layui/layer-vue
|
||||||
|
```
|
||||||
|
|
||||||
|
Before using, you need to mount layui Vue to Vue and introduce index.css style file.
|
||||||
|
|
||||||
|
```
|
||||||
|
import App from './App';
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import layer from '@layui/layer-vue';
|
||||||
|
import '@layui/layer-vue/lib/index.css';
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.use(layer).mount('#app');
|
||||||
|
```
|
||||||
|
|
||||||
|
We support functional invocation:
|
||||||
|
|
||||||
|
```
|
||||||
|
<template>
|
||||||
|
<button type="button" @click="openSuccess">提示消息</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { layer } from "@layui/layer-vue";
|
||||||
|
|
||||||
|
const openSuccess = function () {
|
||||||
|
layer.msg("成功消息", {icon:1, time:1000});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
Componentized calls are also supported:
|
||||||
|
|
||||||
|
```
|
||||||
|
<template>
|
||||||
|
<button @click="changeVisible">显示/隐藏</button>
|
||||||
|
<lay-layer v-model="visible">
|
||||||
|
content
|
||||||
|
</lay-layer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
const changeVisible = () => {
|
||||||
|
visible.value = !visible.value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Open Source Licence
|
||||||
|
|
||||||
|
layui vue is licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
|
```
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright 2021 就眠儀式
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
```
|
1
lib/index.css
Normal file
1
lib/index.css
Normal file
File diff suppressed because one or more lines are too long
2262
lib/layer-vue.es.js
Normal file
2262
lib/layer-vue.es.js
Normal file
File diff suppressed because one or more lines are too long
3
lib/layer-vue.umd.js
Normal file
3
lib/layer-vue.umd.js
Normal file
File diff suppressed because one or more lines are too long
38
package.json
Normal file
38
package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@layui/layer-vue",
|
||||||
|
"version": "1.4.7",
|
||||||
|
"description": "a component library for Vue 3 base on layui-vue",
|
||||||
|
"homepage": "http://www.layui-vue.com",
|
||||||
|
"module": "lib/layer-vue.es.js",
|
||||||
|
"main": "lib/layer-vue.umd.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./lib/layer-vue.es.js",
|
||||||
|
"require": "./lib/layer-vue.umd.js"
|
||||||
|
},
|
||||||
|
"./lib/": "./lib/"
|
||||||
|
},
|
||||||
|
"types": "types/index.d.ts",
|
||||||
|
"style": "lib/index.css",
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build --emptyOutDir && npm run build:types",
|
||||||
|
"build:types": "rimraf types && tsc -d"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"layui-vue",
|
||||||
|
"layui",
|
||||||
|
"vue"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"types"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"current node",
|
||||||
|
"last 2 versions and > 2%",
|
||||||
|
"ie > 10"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/plugin-transform-runtime": "^7.18.5"
|
||||||
|
}
|
||||||
|
}
|
25
shims-vue.d.ts
vendored
Normal file
25
shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
declare module "*.vue" {
|
||||||
|
import { DefineComponent } from "vue";
|
||||||
|
const comp: DefineComponent;
|
||||||
|
export default comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.md" {
|
||||||
|
import { DefineComponent } from "vue";
|
||||||
|
const comp: DefineComponent;
|
||||||
|
export default comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "prismjs";
|
||||||
|
declare module "prismjs/components/index";
|
||||||
|
declare module "escape-html";
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
env: {
|
||||||
|
MODE: string;
|
||||||
|
BASE_URL: string;
|
||||||
|
PROD: boolean;
|
||||||
|
DEV: boolean;
|
||||||
|
SSR: boolean;
|
||||||
|
};
|
||||||
|
}
|
29
src/component/CloseBtn.vue
Normal file
29
src/component/CloseBtn.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "CloseBtn",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineEmits } from "vue";
|
||||||
|
|
||||||
|
export interface CloseBtnProps {
|
||||||
|
closeBtn?: number | string | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CloseBtnProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(["closeHandle"]);
|
||||||
|
|
||||||
|
const closeHandle = () => {
|
||||||
|
emit("closeHandle");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a
|
||||||
|
:class="['layui-layer-ico layui-layer-close layui-layer-close' + closeBtn]"
|
||||||
|
href="javascript:;"
|
||||||
|
@click="closeHandle"
|
||||||
|
></a>
|
||||||
|
</template>
|
28
src/component/Iframe.vue
Normal file
28
src/component/Iframe.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, VNodeTypes } from "vue";
|
||||||
|
export default {
|
||||||
|
name: "Iframe",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
export interface IframeProps {
|
||||||
|
src?: string | Function | object | VNodeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<IframeProps>();
|
||||||
|
|
||||||
|
const src = computed(() => {
|
||||||
|
return props.src as string;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<iframe
|
||||||
|
scrolling="auto"
|
||||||
|
class="layui-layer-iframe"
|
||||||
|
allowtransparency="true"
|
||||||
|
frameborder="0"
|
||||||
|
:src="src"
|
||||||
|
></iframe>
|
||||||
|
</template>
|
63
src/component/Notifiy.vue
Normal file
63
src/component/Notifiy.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layui-layer-notifiy-wrapper" 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, shallowRef } 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 notifyRef = shallowRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
emit("close");
|
||||||
|
};
|
||||||
|
|
||||||
|
function addClass(obj: HTMLElement, cls: string) {
|
||||||
|
//获取 class 内容.
|
||||||
|
let obj_class = obj.className,
|
||||||
|
//判断获取到的 class 是否为空, 如果不为空在前面加个'空格'.
|
||||||
|
blank = obj_class != "" ? " " : "";
|
||||||
|
let added = obj_class + blank + cls; //组合原来的 class 和需要添加的 class.
|
||||||
|
obj.className = added; //替换原来的 class.
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
//此处延迟加载class,以免影响弹出效果
|
||||||
|
if (!notifyRef.value?.parentElement?.parentElement) return;
|
||||||
|
addClass(
|
||||||
|
notifyRef.value?.parentElement?.parentElement,
|
||||||
|
"layui-layer-notifiy-transition"
|
||||||
|
);
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
100
src/component/Photos.vue
Normal file
100
src/component/Photos.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layui-layer-phimg">
|
||||||
|
<img :src="imgList[index].src" />
|
||||||
|
<div class="layui-layer-imgsee" v-if="imgList.length > 0">
|
||||||
|
<span class="layui-layer-imguide" v-if="imgList.length > 1">
|
||||||
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
class="layui-layer-iconext layui-layer-imgprev"
|
||||||
|
@click="changeIndex(-1)"
|
||||||
|
></a>
|
||||||
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
class="layui-layer-iconext layui-layer-imgnext"
|
||||||
|
@click="changeIndex(1)"
|
||||||
|
></a>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="layui-layer-imgbar"
|
||||||
|
v-if="imgList.length > 1 || imgList[index].alt"
|
||||||
|
:style="{ opacity: showLayerImgBar ? 1 : 0 }"
|
||||||
|
>
|
||||||
|
<div class="thumb-row" v-if="ifSetThumb">
|
||||||
|
<div
|
||||||
|
class="thumb-box"
|
||||||
|
v-for="(item, i) in imgList"
|
||||||
|
:key="'thumb-box' + i"
|
||||||
|
@click="index = i"
|
||||||
|
>
|
||||||
|
<img :src="item.thumb" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="thumb-box-border"
|
||||||
|
:style="{
|
||||||
|
left: `calc(calc( calc(100% - ${100 * imgList.length}px) / 2) + ${
|
||||||
|
index * 100
|
||||||
|
}px)`,
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="layui-layer-imgtit" v-else>
|
||||||
|
<span v-if="imgList[index].alt">{{ imgList[index].alt }}</span>
|
||||||
|
<em v-if="imgList.length > 1"
|
||||||
|
>{{ index + 1 }} / {{ imgList.length }}</em
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "Photos",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { watch, ref, onMounted, nextTick, computed } from "vue";
|
||||||
|
|
||||||
|
export interface LayPhotoProps {
|
||||||
|
imgList: { src: string; alt: string; thumb: string }[];
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
const emit = defineEmits(["resetCalculationPohtosArea"]);
|
||||||
|
const props = withDefaults(defineProps<LayPhotoProps>(), {
|
||||||
|
startIndex: 0,
|
||||||
|
});
|
||||||
|
const index = ref(props.startIndex);
|
||||||
|
watch(index, () => {
|
||||||
|
//当图片索引改变的时候 重新计算弹层的大小
|
||||||
|
emit("resetCalculationPohtosArea", index.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeIndex = (step: number) => {
|
||||||
|
let nowIndex = index.value;
|
||||||
|
let next = nowIndex + step;
|
||||||
|
if (next < 0) {
|
||||||
|
next = props.imgList.length - 1;
|
||||||
|
}
|
||||||
|
if (next >= props.imgList.length) {
|
||||||
|
next = 0;
|
||||||
|
}
|
||||||
|
index.value = next;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showLayerImgBar = ref(false);
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
showLayerImgBar.value = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const ifSetThumb = computed(() => {
|
||||||
|
let res = false;
|
||||||
|
props.imgList.forEach((e) => {
|
||||||
|
if (e.thumb) {
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
</script>
|
9
src/component/Resize.vue
Normal file
9
src/component/Resize.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "Resize",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="layui-layer-resize"></span>
|
||||||
|
</template>
|
39
src/component/Shade.vue
Normal file
39
src/component/Shade.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "Shade",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineEmits, StyleValue } from "vue";
|
||||||
|
|
||||||
|
export interface ShadeProps {
|
||||||
|
opacity: string;
|
||||||
|
index: number | Function;
|
||||||
|
visible: boolean | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<ShadeProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(["shadeClick"]);
|
||||||
|
|
||||||
|
const styles = computed<any>(() => {
|
||||||
|
return {
|
||||||
|
opacity: props.opacity,
|
||||||
|
zIndex: props.index,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const shadeClick = () => {
|
||||||
|
emit("shadeClick");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="layui-layer-shade"
|
||||||
|
:style="styles"
|
||||||
|
@click="shadeClick"
|
||||||
|
v-if="visible"
|
||||||
|
></div>
|
||||||
|
</template>
|
26
src/component/Title.vue
Normal file
26
src/component/Title.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "Title",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
export interface HeaderProps {
|
||||||
|
title: string | boolean | Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderContent = function (content: any) {
|
||||||
|
if (typeof content === "function") {
|
||||||
|
return content();
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps<HeaderProps>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="layui-layer-title" style="cursor: move">
|
||||||
|
{{ renderContent(title) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
1
src/component/index.ts
Normal file
1
src/component/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as LayLayer } from "./index.vue"; // layer component
|
712
src/component/index.vue
Normal file
712
src/component/index.vue
Normal file
@ -0,0 +1,712 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LayLayer",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Shade from "./Shade.vue";
|
||||||
|
import Iframe from "./Iframe.vue";
|
||||||
|
import Title from "./Title.vue";
|
||||||
|
import CloseBtn from "./CloseBtn.vue";
|
||||||
|
import Resize from "./Resize.vue";
|
||||||
|
import Photos from "./Photos.vue";
|
||||||
|
import Notifiy from "./Notifiy.vue";
|
||||||
|
import {
|
||||||
|
Ref,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
computed,
|
||||||
|
useSlots,
|
||||||
|
VNodeTypes,
|
||||||
|
nextTick,
|
||||||
|
inject,
|
||||||
|
} from "vue";
|
||||||
|
import {
|
||||||
|
nextId,
|
||||||
|
maxArea,
|
||||||
|
maxOffset,
|
||||||
|
getArea,
|
||||||
|
calculateArea,
|
||||||
|
calculateOffset,
|
||||||
|
calculateContent,
|
||||||
|
calculateType,
|
||||||
|
minArea,
|
||||||
|
minOffset,
|
||||||
|
updateMinArrays,
|
||||||
|
getDrawerAnimationClass,
|
||||||
|
calculateDrawerArea,
|
||||||
|
calculatePhotosArea,
|
||||||
|
calculateNotifOffset,
|
||||||
|
removeNotifiyFromQueen,
|
||||||
|
getNotifyAnimationClass,
|
||||||
|
} from "../utils";
|
||||||
|
import useResize from "../composable/useResize";
|
||||||
|
import useMove from "../composable/useMove";
|
||||||
|
import { zIndexKey } from "../tokens";
|
||||||
|
|
||||||
|
export interface LayerProps {
|
||||||
|
id?: string;
|
||||||
|
title?: string | boolean | Function;
|
||||||
|
icon?: string | number;
|
||||||
|
skin?: string;
|
||||||
|
zIndex?: number | Function;
|
||||||
|
setTop?: boolean;
|
||||||
|
offset?: string[] | string;
|
||||||
|
area?: string[] | "auto";
|
||||||
|
modelValue?: boolean;
|
||||||
|
maxmin?: boolean | string;
|
||||||
|
btn?: Record<string, Function>[] | false;
|
||||||
|
move?: boolean | string;
|
||||||
|
resize?: boolean | string;
|
||||||
|
type?:
|
||||||
|
| 0
|
||||||
|
| 1
|
||||||
|
| 2
|
||||||
|
| 3
|
||||||
|
| 4
|
||||||
|
| 5
|
||||||
|
| 6
|
||||||
|
| "dialog"
|
||||||
|
| "page"
|
||||||
|
| "iframe"
|
||||||
|
| "loading"
|
||||||
|
| "drawer"
|
||||||
|
| "photos"
|
||||||
|
| "notifiy";
|
||||||
|
content?: string | Function | object | VNodeTypes;
|
||||||
|
isHtmlFragment?: boolean;
|
||||||
|
shade?: boolean | string;
|
||||||
|
shadeClose?: boolean | string;
|
||||||
|
shadeOpacity?: string;
|
||||||
|
closeBtn?: boolean | string;
|
||||||
|
btnAlign?: "l" | "c" | "r";
|
||||||
|
time?: number;
|
||||||
|
load?: number;
|
||||||
|
anim?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
isOutAnim?: boolean;
|
||||||
|
destroy?: Function;
|
||||||
|
success?: Function;
|
||||||
|
end?: Function;
|
||||||
|
yes?: Function;
|
||||||
|
yesText?: string;
|
||||||
|
isFunction?: boolean;
|
||||||
|
isMessage?: boolean;
|
||||||
|
appContext?: any;
|
||||||
|
startIndex?: number;
|
||||||
|
imgList?: { src: string; alt: string; thumb: string }[];
|
||||||
|
min?: Function;
|
||||||
|
full?: Function;
|
||||||
|
restore?: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<LayerProps>(), {
|
||||||
|
title: "标题",
|
||||||
|
setTop: false,
|
||||||
|
offset: () => ["50%", "50%"],
|
||||||
|
area: "auto",
|
||||||
|
modelValue: false,
|
||||||
|
maxmin: false,
|
||||||
|
move: true,
|
||||||
|
type: 1,
|
||||||
|
time: 0,
|
||||||
|
shade: true,
|
||||||
|
shadeClose: true,
|
||||||
|
shadeOpacity: "0.1",
|
||||||
|
closeBtn: "1",
|
||||||
|
btnAlign: "r",
|
||||||
|
load: 0,
|
||||||
|
anim: 0,
|
||||||
|
resize: false,
|
||||||
|
isHtmlFragment: false,
|
||||||
|
isOutAnim: true,
|
||||||
|
destroy: () => {},
|
||||||
|
success: () => {},
|
||||||
|
end: () => {},
|
||||||
|
full: () => {},
|
||||||
|
min: () => {},
|
||||||
|
restore: () => {},
|
||||||
|
yesText: "确定",
|
||||||
|
isFunction: false,
|
||||||
|
isMessage: false,
|
||||||
|
startIndex: 0,
|
||||||
|
imgList: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["close", "update:modelValue"]);
|
||||||
|
|
||||||
|
const slots = useSlots();
|
||||||
|
const max: Ref<boolean> = ref(false);
|
||||||
|
const min: Ref<boolean> = ref(false);
|
||||||
|
const id: Ref<string> = ref(props.id || nextId());
|
||||||
|
const layero = ref<HTMLElement | null>(null);
|
||||||
|
const type: number = calculateType(props.type);
|
||||||
|
const area: Ref<string[]> = ref(
|
||||||
|
calculateArea(props.type, props.area, props.offset)
|
||||||
|
);
|
||||||
|
const offset: Ref<string[]> = ref(
|
||||||
|
calculateOffset(props.offset, area.value, props.type)
|
||||||
|
);
|
||||||
|
const contentHeight = ref(
|
||||||
|
calculateContent(props.title, area.value[1], props.btn, type, props.isMessage)
|
||||||
|
);
|
||||||
|
const index: Ref<number | Function> = ref(
|
||||||
|
props.zIndex ?? inject(zIndexKey, 99999)
|
||||||
|
);
|
||||||
|
const visible: Ref<boolean> = ref(false);
|
||||||
|
const first: Ref<boolean> = ref(true);
|
||||||
|
|
||||||
|
const w: Ref<string> = ref(area.value[0]);
|
||||||
|
const h: Ref<string> = ref(area.value[1]);
|
||||||
|
const t: Ref<string> = ref(offset.value[0]);
|
||||||
|
const l: Ref<string> = ref(offset.value[1]);
|
||||||
|
|
||||||
|
const _w: Ref<string> = ref(area.value[0]);
|
||||||
|
const _h: Ref<string> = ref(area.value[0]);
|
||||||
|
const _t: Ref<string> = ref(offset.value[0]);
|
||||||
|
const _l: Ref<string> = ref(offset.value[1]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首次打开
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const firstOpenDelayCalculation = function () {
|
||||||
|
nextTick(async () => {
|
||||||
|
area.value = getArea(layero.value);
|
||||||
|
if (type == 4) {
|
||||||
|
area.value = calculateDrawerArea(props.offset, props.area);
|
||||||
|
}
|
||||||
|
if (type == 5) {
|
||||||
|
area.value = await calculatePhotosArea(
|
||||||
|
props.imgList[props.startIndex].src,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
h.value = area.value[1];
|
||||||
|
_w.value = area.value[0];
|
||||||
|
_l.value = area.value[1];
|
||||||
|
t.value = offset.value[0];
|
||||||
|
l.value = offset.value[1];
|
||||||
|
_t.value = offset.value[0];
|
||||||
|
_l.value = offset.value[1];
|
||||||
|
supportMove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通打开
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const notFirstOpenLayerInit = function () {
|
||||||
|
w.value = _w.value;
|
||||||
|
h.value = _h.value;
|
||||||
|
t.value = _t.value;
|
||||||
|
l.value = _l.value;
|
||||||
|
supportMove();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component 模式, 关闭事件
|
||||||
|
* <p>
|
||||||
|
* 在 Component 模式下, 隐藏前需要恢复正常窗体位置与尺寸, 保存
|
||||||
|
**/
|
||||||
|
const beforeCloseSaveData = function () {
|
||||||
|
if (min.value) minHandle();
|
||||||
|
if (max.value) maxHandle();
|
||||||
|
_w.value = w.value;
|
||||||
|
_h.value = h.value;
|
||||||
|
_t.value = t.value;
|
||||||
|
_l.value = l.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层最大化
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const maxHandle = () => {
|
||||||
|
if (max.value) {
|
||||||
|
w.value = _w.value;
|
||||||
|
h.value = _h.value;
|
||||||
|
t.value = _t.value;
|
||||||
|
l.value = _l.value;
|
||||||
|
props.restore(props.id);
|
||||||
|
} else {
|
||||||
|
_t.value = t.value;
|
||||||
|
_l.value = l.value;
|
||||||
|
_w.value = w.value;
|
||||||
|
_h.value = h.value;
|
||||||
|
w.value = maxArea().w;
|
||||||
|
h.value = maxArea().h;
|
||||||
|
t.value = maxOffset().t;
|
||||||
|
l.value = maxOffset().l;
|
||||||
|
props.full(props.id);
|
||||||
|
}
|
||||||
|
max.value = !max.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层最小化
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const minHandle = () => {
|
||||||
|
let left = 180 * updateMinArrays(id.value, !min.value);
|
||||||
|
if (left > document.documentElement.clientWidth - 180) {
|
||||||
|
left = document.documentElement.clientWidth - 180;
|
||||||
|
}
|
||||||
|
if (min.value) {
|
||||||
|
w.value = _w.value;
|
||||||
|
h.value = _h.value;
|
||||||
|
t.value = _t.value;
|
||||||
|
l.value = _l.value;
|
||||||
|
props.restore(props.id);
|
||||||
|
} else {
|
||||||
|
_w.value = w.value;
|
||||||
|
_h.value = h.value;
|
||||||
|
_t.value = t.value;
|
||||||
|
_l.value = l.value;
|
||||||
|
h.value = minArea().h;
|
||||||
|
w.value = minArea().w;
|
||||||
|
t.value = minOffset(left).t;
|
||||||
|
l.value = minOffset(left).l;
|
||||||
|
props.min(props.id);
|
||||||
|
}
|
||||||
|
min.value = !min.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置弹层
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const reset = function () {
|
||||||
|
if (!first.value) {
|
||||||
|
min.value = false;
|
||||||
|
max.value = false;
|
||||||
|
w.value = area.value[0];
|
||||||
|
h.value = area.value[1];
|
||||||
|
t.value = offset.value[0];
|
||||||
|
l.value = offset.value[1];
|
||||||
|
_w.value = area.value[0];
|
||||||
|
_h.value = area.value[1];
|
||||||
|
_t.value = offset.value[0];
|
||||||
|
_l.value = offset.value[1];
|
||||||
|
}
|
||||||
|
if (!props.modelValue) {
|
||||||
|
emit("update:modelValue", true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听 modalValue 的值
|
||||||
|
* <p>
|
||||||
|
* 只有 Component 模式会触发
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
() => {
|
||||||
|
visible.value = props.modelValue;
|
||||||
|
if (visible.value) {
|
||||||
|
if (first.value) {
|
||||||
|
first.value = false;
|
||||||
|
firstOpenDelayCalculation();
|
||||||
|
} else {
|
||||||
|
notFirstOpenLayerInit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
beforeCloseSaveData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听 visible 值
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
() => {
|
||||||
|
if (visible.value) {
|
||||||
|
if (props.isFunction) {
|
||||||
|
firstOpenDelayCalculation();
|
||||||
|
}
|
||||||
|
props.success();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
() => {
|
||||||
|
if (!visible.value) {
|
||||||
|
props.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高度监听
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* 在发生拖拽时, 需根据弹出层的外层高度计算 content 高度, 需要
|
||||||
|
* 考虑 btn 操作栏, 计算公式:contentHeight = h - btnHeight
|
||||||
|
*
|
||||||
|
* @param h 弹层高度
|
||||||
|
* @param btn 操作栏
|
||||||
|
* @param type 弹层类型
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => h.value,
|
||||||
|
() => {
|
||||||
|
contentHeight.value = calculateContent(
|
||||||
|
props.title,
|
||||||
|
h.value,
|
||||||
|
props.btn,
|
||||||
|
type,
|
||||||
|
props.isMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层类型
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @param isMessage 是消息
|
||||||
|
* @param icon 图标类型
|
||||||
|
*/
|
||||||
|
const boxClasses = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"layui-layer-dialog": type === 0,
|
||||||
|
"layui-layer-page": type === 1,
|
||||||
|
"layui-layer-iframe": type === 2,
|
||||||
|
"layui-layer-loading": type === 3,
|
||||||
|
"layui-layer-drawer": type === 4,
|
||||||
|
"layui-layer-photos": type === 5,
|
||||||
|
"layui-layer-notifiy": type === 6,
|
||||||
|
"layui-layer-msg": props.isMessage,
|
||||||
|
"layui-layer-hui": props.isMessage && !props.icon,
|
||||||
|
},
|
||||||
|
props.skin,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拖拽拉伸
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const supportMove = function () {
|
||||||
|
if (props.move && type != 4) {
|
||||||
|
nextTick(() => {
|
||||||
|
// 拖拽
|
||||||
|
if (!layero.value) return;
|
||||||
|
useMove(layero.value, (left: string, top: string) => {
|
||||||
|
l.value = left;
|
||||||
|
t.value = top;
|
||||||
|
});
|
||||||
|
// 拉伸
|
||||||
|
useResize(layero.value, (width: string, height: string) => {
|
||||||
|
h.value = height;
|
||||||
|
w.value = width;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层样式
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const styles = computed<any>(() => {
|
||||||
|
let style = {
|
||||||
|
top: t.value,
|
||||||
|
left: l.value,
|
||||||
|
width: w.value,
|
||||||
|
height: h.value,
|
||||||
|
zIndex: index.value,
|
||||||
|
};
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层内容
|
||||||
|
* <p>
|
||||||
|
* @param type 类型
|
||||||
|
* @param load 加载动画
|
||||||
|
* @param icon 图标
|
||||||
|
*/
|
||||||
|
const contentClasses = computed(() => {
|
||||||
|
return [
|
||||||
|
type === 3 ? `layui-layer-loading${props.load}` : "",
|
||||||
|
props.icon ? "layui-layer-padding" : "",
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭操作
|
||||||
|
* <p>
|
||||||
|
* @param null
|
||||||
|
*/
|
||||||
|
const closeHandle = () => {
|
||||||
|
emit("close");
|
||||||
|
emit("update:modelValue", false);
|
||||||
|
props.destroy();
|
||||||
|
if (type === 6) {
|
||||||
|
//@ts-ignore
|
||||||
|
removeNotifiyFromQueen(props.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定操作
|
||||||
|
* <p>
|
||||||
|
* @param null
|
||||||
|
*/
|
||||||
|
const yesHandle = () => {
|
||||||
|
if (props.yes != undefined) props.yes();
|
||||||
|
else closeHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遮盖层点击
|
||||||
|
* <p>
|
||||||
|
* @param null
|
||||||
|
*/
|
||||||
|
const shadeHandle = () => {
|
||||||
|
if (props.shadeClose) closeHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容
|
||||||
|
* <p>
|
||||||
|
* @param content 文本 / 方法
|
||||||
|
*/
|
||||||
|
const renderContent = function (content: any) {
|
||||||
|
if (content instanceof Function) {
|
||||||
|
return content();
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹层图标
|
||||||
|
* <p>
|
||||||
|
* @param icon 图标
|
||||||
|
*/
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return ["layui-layer-ico", `layui-layer-ico${props.icon}`];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入场动画
|
||||||
|
* <p>
|
||||||
|
* @param type 类型
|
||||||
|
* @param anim 入场动画
|
||||||
|
*/
|
||||||
|
const enterActiveClass = computed(() => {
|
||||||
|
if (type === 4) {
|
||||||
|
return getDrawerAnimationClass(props.offset);
|
||||||
|
}
|
||||||
|
if (type === 6) {
|
||||||
|
return getNotifyAnimationClass(props.offset);
|
||||||
|
}
|
||||||
|
return `layer-anim layer-anim-0${props.anim}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 离场动画
|
||||||
|
* <p>
|
||||||
|
* @param type 类型
|
||||||
|
* @param isOutAnim 离场动画
|
||||||
|
*/
|
||||||
|
const leaveActiveClass = computed(() => {
|
||||||
|
if (type === 4) {
|
||||||
|
return getDrawerAnimationClass(props.offset, true);
|
||||||
|
}
|
||||||
|
return props.isOutAnim ? `layer-anim-close` : "";
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开弹层
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭弹层
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示遮盖
|
||||||
|
* <p>
|
||||||
|
* @param visible 弹层状态
|
||||||
|
* @param shade 开启遮盖
|
||||||
|
* @param min 最小化
|
||||||
|
*/
|
||||||
|
const shadeVisible = computed(() => {
|
||||||
|
return visible.value && props.shade && !min.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉伸支持
|
||||||
|
* <p>
|
||||||
|
* @param resize 拉伸
|
||||||
|
* @param max 最大化
|
||||||
|
* @param min 最小化
|
||||||
|
*/
|
||||||
|
const showResize = computed(() => {
|
||||||
|
return props.resize && !max.value && !min.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示标题
|
||||||
|
* <p>
|
||||||
|
* @param title 标题
|
||||||
|
* @param type 类型
|
||||||
|
*/
|
||||||
|
const showTitle = computed(() => {
|
||||||
|
return props.title && props.type != 3 && props.type != 5 && props.type != 6;
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 图片弹层重新计算
|
||||||
|
*/
|
||||||
|
const resetCalculationPohtosArea = function (index: number) {
|
||||||
|
nextTick(async () => {
|
||||||
|
area.value = await calculatePhotosArea(props.imgList[index].src, props);
|
||||||
|
offset.value = calculateOffset(props.offset, area.value, props.type);
|
||||||
|
w.value = area.value[0];
|
||||||
|
h.value = area.value[1];
|
||||||
|
t.value = offset.value[0];
|
||||||
|
l.value = offset.value[1];
|
||||||
|
_w.value = area.value[0];
|
||||||
|
_l.value = area.value[1];
|
||||||
|
_t.value = offset.value[0];
|
||||||
|
_l.value = offset.value[1];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ reset, open, close });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 遮盖层 -->
|
||||||
|
<Shade
|
||||||
|
:index="index"
|
||||||
|
:visible="shadeVisible"
|
||||||
|
:opacity="shadeOpacity"
|
||||||
|
@shadeClick="shadeHandle"
|
||||||
|
></Shade>
|
||||||
|
<!-- 动画容器 -->
|
||||||
|
<transition
|
||||||
|
:enter-active-class="enterActiveClass"
|
||||||
|
:leave-active-class="leaveActiveClass"
|
||||||
|
>
|
||||||
|
<!-- 弹出层 -->
|
||||||
|
<div
|
||||||
|
ref="layero"
|
||||||
|
class="layui-layer layui-layer-border"
|
||||||
|
:class="boxClasses"
|
||||||
|
:style="styles"
|
||||||
|
v-if="visible"
|
||||||
|
>
|
||||||
|
<!-- 标题 -->
|
||||||
|
<Title v-if="showTitle" :title="title"></Title>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div
|
||||||
|
class="layui-layer-content"
|
||||||
|
:style="{ height: contentHeight }"
|
||||||
|
:class="contentClasses"
|
||||||
|
>
|
||||||
|
<template v-if="type === 0 || type === 1 || type === 4">
|
||||||
|
<i v-if="icon" :class="iconClass"></i>
|
||||||
|
<slot v-if="slots.default"></slot>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="isHtmlFragment">
|
||||||
|
<span v-html="renderContent(props.content)"></span>
|
||||||
|
</template>
|
||||||
|
<template v-else>{{ renderContent(props.content) }}</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<Iframe v-if="type === 2" :src="props.content"></Iframe>
|
||||||
|
<Photos
|
||||||
|
v-if="type === 5"
|
||||||
|
:imgList="props.imgList"
|
||||||
|
:startIndex="props.startIndex"
|
||||||
|
@resetCalculationPohtosArea="resetCalculationPohtosArea"
|
||||||
|
></Photos>
|
||||||
|
<Notifiy
|
||||||
|
v-if="type === 6"
|
||||||
|
@close="closeHandle"
|
||||||
|
:title="props.title"
|
||||||
|
:content="props.content"
|
||||||
|
:isHtmlFragment="props.isHtmlFragment"
|
||||||
|
:icon="props.icon"
|
||||||
|
:iconClass="iconClass"
|
||||||
|
></Notifiy>
|
||||||
|
</div>
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<span
|
||||||
|
class="layui-layer-setwin"
|
||||||
|
v-if="type != 3 && type != 5 && type != 6"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
v-if="maxmin && !max"
|
||||||
|
class="layui-layer-min"
|
||||||
|
:class="[min ? 'layui-layer-ico layui-layer-maxmin' : '']"
|
||||||
|
href="javascript:;"
|
||||||
|
@click="minHandle"
|
||||||
|
>
|
||||||
|
<cite v-if="!min"></cite>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
v-if="maxmin && !min"
|
||||||
|
class="layui-layer-ico layui-layer-max"
|
||||||
|
:class="[max ? 'layui-layer-maxmin' : '']"
|
||||||
|
href="javascript:;"
|
||||||
|
@click="maxHandle"
|
||||||
|
></a>
|
||||||
|
<CloseBtn
|
||||||
|
v-if="closeBtn != false"
|
||||||
|
:close-btn="closeBtn"
|
||||||
|
@closeHandle="closeHandle"
|
||||||
|
></CloseBtn>
|
||||||
|
</span>
|
||||||
|
<!-- 操作栏 -->
|
||||||
|
<div
|
||||||
|
v-if="((btn && btn.length > 0) || type === 0) && !isMessage"
|
||||||
|
class="layui-layer-btn"
|
||||||
|
:class="[`layui-layer-btn-${btnAlign}`]"
|
||||||
|
>
|
||||||
|
<template v-if="btn && btn.length > 0">
|
||||||
|
<template v-for="(b, index) in btn" :key="index">
|
||||||
|
<a :class="[`layui-layer-btn${index}`]" @click="b.callback(id)">{{
|
||||||
|
b.text
|
||||||
|
}}</a>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="type === 0">
|
||||||
|
<a class="layui-layer-btn0" @click="yesHandle()">{{ yesText }}</a>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<!-- 辅助栏 -->
|
||||||
|
<Resize v-if="showResize"></Resize>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
53
src/composable/useMove.ts
Normal file
53
src/composable/useMove.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const useMove = function (el: HTMLElement, callback: Function) {
|
||||||
|
el.style.position = "fixed";
|
||||||
|
let offsetX: number;
|
||||||
|
let offsetY: number;
|
||||||
|
if (el != null) {
|
||||||
|
el.addEventListener("mousedown", function (event: any) {
|
||||||
|
const path = (event.composedPath && event.composedPath()) || event.path;
|
||||||
|
if (path[0].className === "layui-layer-title") {
|
||||||
|
if (event.button == 0 && el != null) {
|
||||||
|
const lexObj: any = getComputedStyle(el);
|
||||||
|
offsetX =
|
||||||
|
event.pageX - el.offsetLeft + parseInt(lexObj["margin-left"]);
|
||||||
|
offsetY =
|
||||||
|
event.pageY - el.offsetTop + parseInt(lexObj["margin-right"]);
|
||||||
|
const move = function (event: any) {
|
||||||
|
if (el != null) {
|
||||||
|
let x = event.pageX - offsetX;
|
||||||
|
let y = event.pageY - offsetY;
|
||||||
|
if (x < 0) {
|
||||||
|
x = 0;
|
||||||
|
} else if (
|
||||||
|
x >
|
||||||
|
document.documentElement.clientWidth - el.offsetWidth
|
||||||
|
) {
|
||||||
|
x = document.documentElement.clientWidth - el.offsetWidth;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
y = 0;
|
||||||
|
} else if (
|
||||||
|
y >
|
||||||
|
document.documentElement.clientHeight - el.offsetHeight
|
||||||
|
) {
|
||||||
|
y = document.documentElement.clientHeight - el.offsetHeight;
|
||||||
|
}
|
||||||
|
el.style.left = `${x}px`;
|
||||||
|
el.style.top = `${y}px`;
|
||||||
|
callback(el.style.left, el.style.top);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
document.addEventListener("mousemove", move);
|
||||||
|
const stop = function () {
|
||||||
|
document.removeEventListener("mousemove", move);
|
||||||
|
document.removeEventListener("mouseup", stop);
|
||||||
|
};
|
||||||
|
document.addEventListener("mouseup", stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default useMove;
|
35
src/composable/useResize.ts
Normal file
35
src/composable/useResize.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const useMove = function (el: HTMLElement, callback: Function) {
|
||||||
|
if (el != null) {
|
||||||
|
el.addEventListener("mousedown", function (event: any) {
|
||||||
|
const path = (event.composedPath && event.composedPath()) || event.path;
|
||||||
|
if (path[0].className === "layui-layer-resize") {
|
||||||
|
if (event.button == 0 && el != null) {
|
||||||
|
var x = el.offsetLeft;
|
||||||
|
var y = el.offsetTop;
|
||||||
|
const move = function (moveEvent: any) {
|
||||||
|
if (el != null) {
|
||||||
|
var offsetX = moveEvent.clientX;
|
||||||
|
var offsetY = moveEvent.clientY;
|
||||||
|
var w = offsetX - x;
|
||||||
|
var h = offsetY - y;
|
||||||
|
w < 260 && (w = 260);
|
||||||
|
h < 115 && (h = 115);
|
||||||
|
el.style.width = `${w}px`;
|
||||||
|
el.style.height = `${h}px`;
|
||||||
|
callback(el.style.width, el.style.height);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
document.addEventListener("mousemove", move);
|
||||||
|
const stop = function () {
|
||||||
|
document.removeEventListener("mousemove", move);
|
||||||
|
document.removeEventListener("mouseup", stop);
|
||||||
|
};
|
||||||
|
document.addEventListener("mouseup", stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default useMove;
|
270
src/index.ts
Normal file
270
src/index.ts
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
import { render, h, isVNode, getCurrentInstance, AppContext, App } from "vue";
|
||||||
|
import LayLayer from "./component/index.vue";
|
||||||
|
import { InstallOptions } from "./types";
|
||||||
|
import { zIndexKey } from "./tokens";
|
||||||
|
import { nextId, removeNotifiyFromQueen } from "./utils";
|
||||||
|
|
||||||
|
// 实例队列
|
||||||
|
const layerInstance: any = [];
|
||||||
|
|
||||||
|
// 新增实例
|
||||||
|
const addInstance = (instance: any) => {
|
||||||
|
layerInstance.push(instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除实例
|
||||||
|
const delInstance = (id: any) => {
|
||||||
|
layerInstance.forEach((item: any, index: number) => {
|
||||||
|
if (item.modalContainer.id === id) {
|
||||||
|
// 删除元素
|
||||||
|
layerInstance.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空实例
|
||||||
|
const cleanInstance = () => {
|
||||||
|
layerInstance.splice(0, layerInstance.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 是否存在
|
||||||
|
const isExist = (id: any) => {
|
||||||
|
let b = false;
|
||||||
|
layerInstance.forEach((item: any, index: number) => {
|
||||||
|
if (item.modalContainer.id == id) {
|
||||||
|
b = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findById = (id: any) => {
|
||||||
|
let instance = null;
|
||||||
|
layerInstance.forEach((item: any, index: number) => {
|
||||||
|
if (item.modalContainer.id === id) {
|
||||||
|
instance = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 聚合 modal 配置
|
||||||
|
const mergeOption = (option: any, defaultOption: any) => {
|
||||||
|
if (option) defaultOption = Object.assign(defaultOption, option);
|
||||||
|
return defaultOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建 modal 容器
|
||||||
|
const createContainer = (options: any) => {
|
||||||
|
const modalContainer = document.createElement("div");
|
||||||
|
modalContainer.id = options.id;
|
||||||
|
document.body.appendChild(modalContainer);
|
||||||
|
return modalContainer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// modal 子 VNode
|
||||||
|
const modalChildrenVNode = (content: any) => {
|
||||||
|
if (typeof content === "function") {
|
||||||
|
return isVNode(content()) ? { default: () => content() } : undefined;
|
||||||
|
}
|
||||||
|
return isVNode(content) ? { default: () => content } : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const layer = {
|
||||||
|
_context: <AppContext | null>null,
|
||||||
|
|
||||||
|
// 页面
|
||||||
|
open: (option: any, callback?: Function) => {
|
||||||
|
let defaultOption = {};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
// 抽屉
|
||||||
|
drawer: (option: any, callback?: Function) => {
|
||||||
|
let defaultOption = {
|
||||||
|
type: "drawer",
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
// 消息
|
||||||
|
msg: (message: string, option?: any, callback?: Function) => {
|
||||||
|
let defaultOption = {
|
||||||
|
type: 0,
|
||||||
|
title: false,
|
||||||
|
content: message,
|
||||||
|
closeBtn: false,
|
||||||
|
shadeClose: false,
|
||||||
|
isMessage: true,
|
||||||
|
shade: false,
|
||||||
|
time: 1000,
|
||||||
|
btn: false,
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
// 加载
|
||||||
|
load: (load: number, option?: any, callback?: Function) => {
|
||||||
|
let defaultOption = {
|
||||||
|
type: 3,
|
||||||
|
load: load,
|
||||||
|
anim: 5,
|
||||||
|
isOutAnim: false,
|
||||||
|
shadeClose: false,
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
// 确认
|
||||||
|
confirm: (msg: string, option?: any, callback?: Function) => {
|
||||||
|
let defaultOption = {
|
||||||
|
type: 0,
|
||||||
|
content: msg,
|
||||||
|
shadeClose: false,
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
//图片预览
|
||||||
|
photos: (option: any, callback?: Function) => {
|
||||||
|
if (typeof option === "string") {
|
||||||
|
option = {
|
||||||
|
imgList: [{ src: option }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let defaultOption = {
|
||||||
|
type: 5,
|
||||||
|
anim: 2,
|
||||||
|
startIndex: 0,
|
||||||
|
isOutAnim: true,
|
||||||
|
shadeClose: true,
|
||||||
|
shadeOpacity: "0.7",
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
//通知
|
||||||
|
notifiy: (option: any = {}, callback?: Function) => {
|
||||||
|
option.type = 6;
|
||||||
|
let defaultOption = {
|
||||||
|
offset: "rt",
|
||||||
|
time: 2000,
|
||||||
|
area: "auto",
|
||||||
|
shade: false,
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
|
// 创建弹出层
|
||||||
|
create: (option: any, defaultOption: any, callback?: Function) => {
|
||||||
|
// 销毁定时
|
||||||
|
let timer: NodeJS.Timeout;
|
||||||
|
// 聚合配置 Opt
|
||||||
|
const options = mergeOption(option, defaultOption);
|
||||||
|
// 生成唯一标识
|
||||||
|
if (options.hasOwnProperty("id")) {
|
||||||
|
// 判断 id 存在, 并销毁窗体
|
||||||
|
layer.close(options.id);
|
||||||
|
} else {
|
||||||
|
// 生成新的唯一标识
|
||||||
|
options.id = nextId();
|
||||||
|
}
|
||||||
|
// 创建容器 Dom
|
||||||
|
const modalContainer = createContainer(options);
|
||||||
|
// 创建虚拟 Dom
|
||||||
|
const modalInstance = h(
|
||||||
|
LayLayer,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
isFunction: true,
|
||||||
|
destroy() {
|
||||||
|
clearTimeout(timer);
|
||||||
|
modalInstance.component?.exposed?.close();
|
||||||
|
setTimeout(() => {
|
||||||
|
render(null, modalContainer);
|
||||||
|
// 清空 dom
|
||||||
|
if (document.body.contains(modalContainer)) {
|
||||||
|
document.body.removeChild(modalContainer);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
delInstance(modalContainer.id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modalChildrenVNode(options.content)
|
||||||
|
);
|
||||||
|
modalInstance.appContext = options.appContext || layer._context;
|
||||||
|
// 将虚拟 dom 渲染到 dom 容器
|
||||||
|
render(modalInstance, modalContainer);
|
||||||
|
// 调用 open 函数
|
||||||
|
modalInstance.component?.exposed?.open();
|
||||||
|
// 延时 time 销毁
|
||||||
|
if (
|
||||||
|
defaultOption &&
|
||||||
|
defaultOption.time != undefined &&
|
||||||
|
defaultOption.time != 0
|
||||||
|
) {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
modalInstance.component?.exposed?.close();
|
||||||
|
if (callback) callback(modalContainer.id);
|
||||||
|
setTimeout(() => {
|
||||||
|
render(null, modalContainer);
|
||||||
|
if (document.body.contains(modalContainer)) {
|
||||||
|
document.body.removeChild(modalContainer);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
// 销毁实例
|
||||||
|
delInstance(modalContainer.id);
|
||||||
|
//Notifiy特殊处理
|
||||||
|
if (options.type === 6) {
|
||||||
|
removeNotifiyFromQueen(options.id);
|
||||||
|
}
|
||||||
|
}, defaultOption.time);
|
||||||
|
}
|
||||||
|
// 维护实例
|
||||||
|
addInstance({ modalContainer, modalInstance });
|
||||||
|
// 返回实例
|
||||||
|
return modalContainer.id;
|
||||||
|
},
|
||||||
|
// 关闭弹出层
|
||||||
|
close: (id: any) => {
|
||||||
|
if (id != null && isExist(id)) {
|
||||||
|
// 找到这个实例
|
||||||
|
const instance: any = findById(id);
|
||||||
|
instance.modalInstance.component?.exposed?.close();
|
||||||
|
setTimeout(() => {
|
||||||
|
render(null, instance.modalContainer);
|
||||||
|
if (document.body.contains(instance.modalContainer))
|
||||||
|
document.body.removeChild(instance.modalContainer);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
// 销毁实例
|
||||||
|
delInstance(id);
|
||||||
|
},
|
||||||
|
// 关闭所有弹出层
|
||||||
|
closeAll: () => {
|
||||||
|
layerInstance.forEach((item: any) => {
|
||||||
|
item.modalInstance.component?.exposed?.close();
|
||||||
|
setTimeout(() => {
|
||||||
|
render(null, item.modalContainer);
|
||||||
|
if (document.body.contains(item.modalContainer))
|
||||||
|
document.body.removeChild(item.modalContainer);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
// 清空实例
|
||||||
|
cleanInstance();
|
||||||
|
},
|
||||||
|
// 重置位置
|
||||||
|
reset: (instance: any) => {
|
||||||
|
instance.modalInstance.component?.exposed?.reset();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全局安装
|
||||||
|
const install = (app: App, options?: InstallOptions): void => {
|
||||||
|
layer._context = app._context;
|
||||||
|
app.component(LayLayer.name, LayLayer);
|
||||||
|
app.config.globalProperties.$layer = layer;
|
||||||
|
if (options) {
|
||||||
|
app.provide(zIndexKey, options.zIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { layer, LayLayer };
|
||||||
|
|
||||||
|
export default { install };
|
||||||
|
|
||||||
|
import "./theme/index.css";
|
||||||
|
import { getSystemErrorMap } from "util";
|
BIN
src/theme/icon-ext.png
Normal file
BIN
src/theme/icon-ext.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
src/theme/icon.png
Normal file
BIN
src/theme/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
1128
src/theme/index.css
Normal file
1128
src/theme/index.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/theme/loading-0.gif
Normal file
BIN
src/theme/loading-0.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
src/theme/loading-1.gif
Normal file
BIN
src/theme/loading-1.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 701 B |
BIN
src/theme/loading-2.gif
Normal file
BIN
src/theme/loading-2.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
1
src/tokens/index.ts
Normal file
1
src/tokens/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const zIndexKey = Symbol("zIndex");
|
3
src/types/index.ts
Normal file
3
src/types/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export type StringObject = Record<string, unknown>;
|
||||||
|
|
||||||
|
export interface InstallOptions extends StringObject {}
|
416
src/utils/index.ts
Normal file
416
src/utils/index.ts
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
import { title } from "process";
|
||||||
|
import { layer } from "../index";
|
||||||
|
|
||||||
|
// 随机数
|
||||||
|
export function nextId() {
|
||||||
|
var s: any = [];
|
||||||
|
var hexDigits = "0123456789abcdef";
|
||||||
|
for (var i = 0; i < 36; i++) {
|
||||||
|
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
||||||
|
}
|
||||||
|
s[14] = "4";
|
||||||
|
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
|
||||||
|
s[8] = s[13] = s[18] = s[23] = "-";
|
||||||
|
var uuid = s.join("");
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateMinArea(minArea: any) {
|
||||||
|
if (!minArea) {
|
||||||
|
return ["120px", "100px"];
|
||||||
|
}
|
||||||
|
if (typeof minArea == "string") {
|
||||||
|
return [minArea];
|
||||||
|
}
|
||||||
|
return [minArea];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateArea(type: any, area: any, offset: any) {
|
||||||
|
return type != "drawer"
|
||||||
|
? calculateBaseArea(area)
|
||||||
|
: calculateDrawerArea(offset, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算宽高
|
||||||
|
// @param area
|
||||||
|
// @param type
|
||||||
|
// @return 正确宽高
|
||||||
|
export function calculateBaseArea(area: any) {
|
||||||
|
if (area === "auto") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
if (typeof area == "string") {
|
||||||
|
return [area];
|
||||||
|
}
|
||||||
|
return [...area];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽屉宽/高
|
||||||
|
export function calculateDrawerArea(
|
||||||
|
offset: any,
|
||||||
|
drawerArea: string[] | string = "30%"
|
||||||
|
) {
|
||||||
|
if (drawerArea instanceof Array) {
|
||||||
|
return drawerArea;
|
||||||
|
}
|
||||||
|
if (drawerArea === "auto") {
|
||||||
|
drawerArea = "30%";
|
||||||
|
}
|
||||||
|
if (offset === "l" || offset === "r") {
|
||||||
|
return [drawerArea, "100%"];
|
||||||
|
} else if (offset === "t" || offset === "b") {
|
||||||
|
return ["100%", drawerArea];
|
||||||
|
}
|
||||||
|
return [drawerArea, "100%"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算偏移
|
||||||
|
// @param offset
|
||||||
|
// @param domSize
|
||||||
|
// @return 正确位置
|
||||||
|
export function calculateOffset(offset: any, area: any, type: any) {
|
||||||
|
var arr = ["t", "r", "b", "l", "lt", "lb", "rt", "rb"];
|
||||||
|
var t = offset[0];
|
||||||
|
var l = offset[1];
|
||||||
|
if (offset instanceof Array && type === "drawer") {
|
||||||
|
offset = "r";
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
if (arr.indexOf(offset) > -1) {
|
||||||
|
t = "50%";
|
||||||
|
l = "50%";
|
||||||
|
}
|
||||||
|
// 预备处理
|
||||||
|
if (arr.indexOf(offset) != -1 || t.indexOf("%") > -1)
|
||||||
|
t = "calc(" + t + " - (" + (area === "auto" ? "100px" : area[1]) + "/2 ))";
|
||||||
|
if (arr.indexOf(offset) != -1 || l.indexOf("%") > -1)
|
||||||
|
l = "calc(" + l + " - (" + (area === "auto" ? "100px" : area[0]) + "/2 ))";
|
||||||
|
// 关键字处理
|
||||||
|
if (offset === "t") t = "0px";
|
||||||
|
else if (offset === "r") l = "calc(100% - " + area[0] + ")";
|
||||||
|
else if (offset === "b") t = "calc(100% - " + area[1] + ")";
|
||||||
|
else if (offset === "l") l = "0px";
|
||||||
|
else if (offset === "lt") {
|
||||||
|
t = "0px";
|
||||||
|
l = "0px";
|
||||||
|
} else if (offset === "lb") {
|
||||||
|
t = "calc(100% - " + area[1] + ")";
|
||||||
|
l = "0px";
|
||||||
|
} else if (offset === "rt") {
|
||||||
|
t = "0px";
|
||||||
|
l = "calc(100% - " + area[0] + ")";
|
||||||
|
} else if (offset === "rb") {
|
||||||
|
t = "calc(100% - " + area[1] + ")";
|
||||||
|
l = "calc(100% - " + area[0] + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回位置
|
||||||
|
return [t, l];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 窗体类型
|
||||||
|
export function calculateType(modalType: number | string) {
|
||||||
|
if (modalType === "dialog" || modalType == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (modalType === "page" || modalType == 1) {
|
||||||
|
return 1;
|
||||||
|
} else if (modalType === "iframe" || modalType == 2) {
|
||||||
|
return 2;
|
||||||
|
} else if (modalType === "loading" || modalType == 3) {
|
||||||
|
return 3;
|
||||||
|
} else if (modalType === "drawer" || modalType == 4) {
|
||||||
|
return 4;
|
||||||
|
} else if (modalType === "photos" || modalType == 5) {
|
||||||
|
return 5;
|
||||||
|
} else if (modalType === "notifiy" || modalType == 6) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算高度
|
||||||
|
// @param height 高度
|
||||||
|
// @param btn 操作集合
|
||||||
|
export function calculateContent(
|
||||||
|
title: any,
|
||||||
|
height: any,
|
||||||
|
btn: any,
|
||||||
|
type: any,
|
||||||
|
isMessage?: boolean
|
||||||
|
) {
|
||||||
|
if (height && height.indexOf("%") != -1) {
|
||||||
|
height = "100%";
|
||||||
|
}
|
||||||
|
if (btn && btn.length > 0) {
|
||||||
|
if (type == 0) {
|
||||||
|
if (title) {
|
||||||
|
return "calc(" + height + " - 137px)";
|
||||||
|
} else {
|
||||||
|
return "calc(" + height + " - 86px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 1 || type == 4) {
|
||||||
|
if (title) {
|
||||||
|
return "calc(" + height + " - 102px)";
|
||||||
|
} else {
|
||||||
|
return "calc(" + height + " - 51px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 2) {
|
||||||
|
if (title) {
|
||||||
|
return "calc(" + height + " - 102px)";
|
||||||
|
} else {
|
||||||
|
return "calc(" + height + " - 51px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (type == 0) {
|
||||||
|
if (title) {
|
||||||
|
return isMessage ? "" : "calc(" + height + " - 137px)";
|
||||||
|
} else {
|
||||||
|
return isMessage ? "" : "calc(" + height + " - 86px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 1 || type == 4) {
|
||||||
|
if (title) {
|
||||||
|
return "calc(" + height + " - 51px)";
|
||||||
|
} else {
|
||||||
|
return "calc(" + height + " - 0px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 2) {
|
||||||
|
if (title) {
|
||||||
|
return "calc(" + height + " - 51px)";
|
||||||
|
} else {
|
||||||
|
return "calc(" + height + " - 0px)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尺寸常量
|
||||||
|
export function maxArea() {
|
||||||
|
return { w: "100%", h: "100%" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始位置
|
||||||
|
export function maxOffset() {
|
||||||
|
return { t: "0px", l: "0px" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最小化尺寸
|
||||||
|
export function minArea() {
|
||||||
|
return { w: "180px", h: "51px" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最小化位置
|
||||||
|
export function minOffset(left: any) {
|
||||||
|
return { t: "calc(100% - 51px)", l: left + "px" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 元素位置
|
||||||
|
// @param x 横坐标
|
||||||
|
// @param y 纵坐标
|
||||||
|
export function getPosition(dom: any) {
|
||||||
|
return { x: dom?.style.left, y: dom?.style.top };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 元素宽高
|
||||||
|
export function getArea(dom: any) {
|
||||||
|
// @ts-ignore
|
||||||
|
let width = getComputedStyle(dom, null).width;
|
||||||
|
// @ts-ignore
|
||||||
|
let height = getComputedStyle(dom, null).height;
|
||||||
|
return [width, height];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最小化的队列
|
||||||
|
let minArrays: Array<String> = [];
|
||||||
|
|
||||||
|
// 更新最小化队列
|
||||||
|
export function updateMinArrays(id: string, state: Boolean) {
|
||||||
|
var i = 0;
|
||||||
|
if (state) {
|
||||||
|
const index = minArrays.findIndex((v) => v === undefined);
|
||||||
|
if (index === -1) {
|
||||||
|
minArrays.push(id);
|
||||||
|
i = minArrays.length - 1;
|
||||||
|
} else {
|
||||||
|
minArrays[index] = id;
|
||||||
|
i = index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete minArrays[minArrays.findIndex((v) => v == id)];
|
||||||
|
i = -1;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽屉动画类
|
||||||
|
export function getDrawerAnimationClass(offset: any, isClose: boolean = false) {
|
||||||
|
const prefix = "layer-drawer-anim layer-anim";
|
||||||
|
let suffix = "rl";
|
||||||
|
if (offset === "l") {
|
||||||
|
suffix = "lr";
|
||||||
|
} else if (offset === "r") {
|
||||||
|
suffix = "rl";
|
||||||
|
} else if (offset === "t") {
|
||||||
|
suffix = "tb";
|
||||||
|
} else if (offset === "b") {
|
||||||
|
suffix = "bt";
|
||||||
|
}
|
||||||
|
return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
//计算图片大小 并缩放
|
||||||
|
export async function calculatePhotosArea(
|
||||||
|
url: string,
|
||||||
|
options: object
|
||||||
|
): Promise<Array<string>> {
|
||||||
|
let img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (img.complete) {
|
||||||
|
resolve(area(img));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layerId = layer.load(2);
|
||||||
|
img.onload = () => {
|
||||||
|
layer.close(layerId);
|
||||||
|
resolve(area(img));
|
||||||
|
};
|
||||||
|
img.onerror = () => {
|
||||||
|
layer.close(layerId);
|
||||||
|
layer.msg("图片加载失败");
|
||||||
|
reject(false);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function area(img: { width: number; height: number }) {
|
||||||
|
var imgarea = [img.width, img.height];
|
||||||
|
var winarea = [window.innerWidth - 250, window.innerHeight - 250];
|
||||||
|
//如果 实际图片的宽或者高比 屏幕大(那么进行缩放)
|
||||||
|
if (imgarea[0] > winarea[0] || imgarea[1] > winarea[1]) {
|
||||||
|
let wh = [imgarea[0] / winarea[0], imgarea[1] / winarea[1]]; //取宽度缩放比例、高度缩放比例
|
||||||
|
if (wh[0] > wh[1]) {
|
||||||
|
//取缩放比例最大的进行缩放
|
||||||
|
imgarea[0] = imgarea[0] / wh[0];
|
||||||
|
imgarea[1] = imgarea[1] / wh[0];
|
||||||
|
} else if (wh[0] < wh[1]) {
|
||||||
|
imgarea[0] = imgarea[0] / wh[1];
|
||||||
|
imgarea[1] = imgarea[1] / wh[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
(window as any).NotifiyQueen = (window as any).NotifiyQueen || [];
|
||||||
|
let notifiyQueen = (window as any).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) {
|
||||||
|
// 间隙
|
||||||
|
let transOffsetTop = 15;
|
||||||
|
// 删除项的高度
|
||||||
|
let notifiyDom = document.getElementById(layerId)?.firstElementChild
|
||||||
|
?.firstElementChild as HTMLElement;
|
||||||
|
let offsetHeight = notifiyDom.offsetHeight;
|
||||||
|
(window as any).NotifiyQueen = (window as any).NotifiyQueen || [];
|
||||||
|
let notifiyQueen = (window as any).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 as HTMLElement;
|
||||||
|
if (offsetType === "rt" || offsetType === "lt") {
|
||||||
|
dom.style["top"] =
|
||||||
|
parseFloat(dom.style["top"]) - transOffsetTop - offsetHeight + "px";
|
||||||
|
} else {
|
||||||
|
let bottom =
|
||||||
|
parseFloat(dom.style["top"].split(" - ")[1]) -
|
||||||
|
transOffsetTop -
|
||||||
|
offsetHeight;
|
||||||
|
dom.style["top"] = "calc(100vh - " + bottom + "px)";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
notifiyQueen.splice(index, 1); //删除
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify动画类
|
||||||
|
export function getNotifyAnimationClass(offset: any) {
|
||||||
|
const prefix = "layer-drawer-anim layer-anim";
|
||||||
|
let suffix = "";
|
||||||
|
if (offset === "lt" || offset === "lb") {
|
||||||
|
suffix = "lr";
|
||||||
|
} else {
|
||||||
|
suffix = "rl";
|
||||||
|
}
|
||||||
|
return `${prefix}-${suffix}`;
|
||||||
|
}
|
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es2015",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"declarationDir": "types",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"lib": ["ESNext","DOM"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*","shims-vue.d.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
1
types/component/index.d.ts
vendored
Normal file
1
types/component/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as LayLayer } from "./index.vue";
|
2
types/composable/useMove.d.ts
vendored
Normal file
2
types/composable/useMove.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
declare const useMove: (el: HTMLElement, callback: Function) => void;
|
||||||
|
export default useMove;
|
2
types/composable/useResize.d.ts
vendored
Normal file
2
types/composable/useResize.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
declare const useMove: (el: HTMLElement, callback: Function) => void;
|
||||||
|
export default useMove;
|
23
types/index.d.ts
vendored
Normal file
23
types/index.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { AppContext, App } from "vue";
|
||||||
|
import LayLayer from "./component/index.vue";
|
||||||
|
import { InstallOptions } from "./types";
|
||||||
|
declare const layer: {
|
||||||
|
_context: AppContext | null;
|
||||||
|
open: (option: any, callback?: Function) => string;
|
||||||
|
drawer: (option: any, callback?: Function) => string;
|
||||||
|
msg: (message: string, option?: any, callback?: Function) => string;
|
||||||
|
load: (load: number, option?: any, callback?: Function) => string;
|
||||||
|
confirm: (msg: string, option?: any, callback?: Function) => string;
|
||||||
|
photos: (option: any, callback?: Function) => string;
|
||||||
|
notifiy: (option?: any, callback?: Function) => string;
|
||||||
|
create: (option: any, defaultOption: any, callback?: Function) => string;
|
||||||
|
close: (id: any) => void;
|
||||||
|
closeAll: () => void;
|
||||||
|
reset: (instance: any) => void;
|
||||||
|
};
|
||||||
|
export { layer, LayLayer };
|
||||||
|
declare const _default: {
|
||||||
|
install: (app: App<any>, options?: InstallOptions | undefined) => void;
|
||||||
|
};
|
||||||
|
export default _default;
|
||||||
|
import "./theme/index.css";
|
1
types/tokens/index.d.ts
vendored
Normal file
1
types/tokens/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare const zIndexKey: unique symbol;
|
3
types/types/index.d.ts
vendored
Normal file
3
types/types/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export declare type StringObject = Record<string, unknown>;
|
||||||
|
export interface InstallOptions extends StringObject {
|
||||||
|
}
|
35
types/utils/index.d.ts
vendored
Normal file
35
types/utils/index.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
export declare function nextId(): any;
|
||||||
|
export declare function calculateMinArea(minArea: any): any[];
|
||||||
|
export declare function calculateArea(type: any, area: any, offset: any): any[];
|
||||||
|
export declare function calculateBaseArea(area: any): any[];
|
||||||
|
export declare function calculateDrawerArea(offset: any, drawerArea?: string[] | string): string[];
|
||||||
|
export declare function calculateOffset(offset: any, area: any, type: any): any[];
|
||||||
|
export declare function calculateType(modalType: number | string): 1 | 0 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
export declare function calculateContent(title: any, height: any, btn: any, type: any, isMessage?: boolean): string | undefined;
|
||||||
|
export declare function maxArea(): {
|
||||||
|
w: string;
|
||||||
|
h: string;
|
||||||
|
};
|
||||||
|
export declare function maxOffset(): {
|
||||||
|
t: string;
|
||||||
|
l: string;
|
||||||
|
};
|
||||||
|
export declare function minArea(): {
|
||||||
|
w: string;
|
||||||
|
h: string;
|
||||||
|
};
|
||||||
|
export declare function minOffset(left: any): {
|
||||||
|
t: string;
|
||||||
|
l: string;
|
||||||
|
};
|
||||||
|
export declare function getPosition(dom: any): {
|
||||||
|
x: any;
|
||||||
|
y: any;
|
||||||
|
};
|
||||||
|
export declare function getArea(dom: any): string[];
|
||||||
|
export declare function updateMinArrays(id: string, state: Boolean): number;
|
||||||
|
export declare function getDrawerAnimationClass(offset: any, isClose?: boolean): string;
|
||||||
|
export declare function calculatePhotosArea(url: string, options: object): Promise<Array<string>>;
|
||||||
|
export declare function calculateNotifOffset(offset: any, area: any, layerId: string): string[];
|
||||||
|
export declare function removeNotifiyFromQueen(layerId: string): void;
|
||||||
|
export declare function getNotifyAnimationClass(offset: any): string;
|
55
vite.config.ts
Normal file
55
vite.config.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import { name } from "./package.json";
|
||||||
|
import { babel } from "@rollup/plugin-babel";
|
||||||
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const camelize = (name: string) =>
|
||||||
|
name.replace(/(^|-)(\w)/g, (a, b, c) => c.toUpperCase());
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"/@src": path.resolve(__dirname, "src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [vue()],
|
||||||
|
build: {
|
||||||
|
target: "es2015",
|
||||||
|
outDir: path.resolve(__dirname, "lib"),
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, "src/index.ts"),
|
||||||
|
name: camelize(name),
|
||||||
|
},
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
drop_console: true,
|
||||||
|
drop_debugger: true,
|
||||||
|
pure_funcs: ["console.log"],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
comments: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
exports: "named",
|
||||||
|
globals: (id: string) => {
|
||||||
|
const name = id.replace(/^@/, "").split("/")[0];
|
||||||
|
return camelize(name);
|
||||||
|
},
|
||||||
|
assetFileNames: "index.css",
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
babel({
|
||||||
|
babelHelpers: "runtime",
|
||||||
|
exclude: "node_modules/**",
|
||||||
|
extensions: [".js", ".jsx", ".ts", ".tsx", ".vue"],
|
||||||
|
presets: ["@babel/preset-env", "@babel/preset-typescript"],
|
||||||
|
plugins: [["@babel/plugin-transform-runtime"]],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
external: ["vue"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user