perf(layer): 初步集成 layer 弹层, 新增 useMove 拖拽 hooks
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -462,16 +462,16 @@ html #layuicss-layer {
|
||||
|
||||
.layui-layer-btn {
|
||||
text-align: right;
|
||||
padding: 0 15px 12px;
|
||||
padding: 8px 15px 12px;
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.layui-layer-btn a {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
margin: 5px 5px 0;
|
||||
height: 33px;
|
||||
line-height: 33px;
|
||||
margin: 0px 5px 0;
|
||||
padding: 0 15px;
|
||||
border: 1px solid #dedede;
|
||||
background-color: #fff;
|
||||
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,5 +1,6 @@
|
||||
import { ref, onMounted, onUnmounted, Ref } from 'vue'
|
||||
|
||||
const x = undefined
|
||||
// 案例详见 dropdown.vue
|
||||
const useClickOutside = (elementRef: Ref<HTMLElement | null>) => {
|
||||
// 设置一个导出值
|
||||
|
||||
25
src/hooks/useFullScreen.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ref, onMounted, onUnmounted, Ref } from 'vue'
|
||||
|
||||
const useFullScreen = () => {
|
||||
const isFullScreen = ref(false)
|
||||
|
||||
const fullScreen = function () {
|
||||
var docElm = document.documentElement
|
||||
switch (!isFullScreen.value) {
|
||||
case true:
|
||||
if (docElm.requestFullscreen) {
|
||||
docElm.requestFullscreen()
|
||||
}
|
||||
break
|
||||
case false:
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
break
|
||||
}
|
||||
isFullScreen.value = !isFullScreen.value
|
||||
}
|
||||
|
||||
return { isFullScreen, fullScreen }
|
||||
}
|
||||
export default useFullScreen
|
||||
103
src/hooks/useMove.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
function useMove(el: any) {
|
||||
el.style.position = 'fixed'
|
||||
let offsetX: number,
|
||||
offsetY: number,
|
||||
oL: number,
|
||||
oT: number,
|
||||
oLeft: number,
|
||||
oTop: number
|
||||
const browser = {
|
||||
versions: (function () {
|
||||
const u = navigator.userAgent
|
||||
return {
|
||||
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //判断设备
|
||||
// ..... 其他设备信息
|
||||
}
|
||||
})(),
|
||||
}
|
||||
if (!browser.versions.mobile) {
|
||||
//Pc
|
||||
if (el != null) {
|
||||
el.addEventListener('mousedown', function (event: any) {
|
||||
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'
|
||||
}
|
||||
return false
|
||||
}
|
||||
document.addEventListener('mousemove', move)
|
||||
const stop = function () {
|
||||
document.removeEventListener('mousemove', move)
|
||||
document.removeEventListener('mouseup', stop)
|
||||
}
|
||||
document.addEventListener('mouseup', stop)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
//Mobile
|
||||
if (el != null) {
|
||||
const maxW = document.body.clientWidth - el.offsetWidth
|
||||
const maxH = document.body.clientHeight - el.offsetHeight
|
||||
const defaultEvent = function (e: any) {
|
||||
e.preventDefault()
|
||||
}
|
||||
el.addEventListener('touchstart', function (e: any) {
|
||||
const ev = e || window.event
|
||||
const touch = ev.targetTouches[0]
|
||||
oL = touch.clientX - el.offsetLeft
|
||||
oT = touch.clientY - el.offsetTop
|
||||
document.addEventListener('touchmove', defaultEvent, false)
|
||||
el.addEventListener('touchmove', function (e: any) {
|
||||
const ev = e || window.event
|
||||
const touch = ev.targetTouches[0]
|
||||
oLeft = touch.clientX - oL
|
||||
oTop = touch.clientY - oT
|
||||
if (oLeft < 0) {
|
||||
oLeft = 0
|
||||
} else if (oLeft >= maxW) {
|
||||
oLeft = maxW
|
||||
}
|
||||
if (oTop < 0) {
|
||||
oTop = 0
|
||||
} else if (oTop >= maxH) {
|
||||
oTop = maxH
|
||||
}
|
||||
el.style.left = oLeft + 'px'
|
||||
el.style.top = oTop + 'px'
|
||||
})
|
||||
el.addEventListener('touchend', function () {
|
||||
document.removeEventListener('touchmove', defaultEvent)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default useMove
|
||||
@@ -59,6 +59,7 @@ import LaySlider from './module/slider/index'
|
||||
import LayCarousel from './module/carousel/index'
|
||||
import LayCarouselItem from './module/carouselItem/index'
|
||||
import LayColorPicker from './module/colorPicker/index'
|
||||
import LayLayer from './module/layer/index'
|
||||
|
||||
const components: Record<string, IDefineComponent> = {
|
||||
LayRadio,
|
||||
@@ -117,6 +118,7 @@ const components: Record<string, IDefineComponent> = {
|
||||
LayCarousel,
|
||||
LayCarouselItem,
|
||||
LayColorPicker,
|
||||
LayLayer,
|
||||
}
|
||||
|
||||
const install = (app: App, options?: InstallOptions): void => {
|
||||
@@ -186,6 +188,7 @@ export {
|
||||
LayCarousel,
|
||||
LayCarouselItem,
|
||||
LayColorPicker,
|
||||
LayLayer,
|
||||
install,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,18 +25,23 @@
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup name="LayButton" lang="ts">
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayButton',
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props =
|
||||
defineProps<{
|
||||
type?: string
|
||||
size?: string
|
||||
fluid?: boolean
|
||||
radius?: boolean
|
||||
border?: string
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
nativeType?: string
|
||||
}>()
|
||||
const props = defineProps<{
|
||||
type?: string
|
||||
size?: string
|
||||
fluid?: boolean
|
||||
radius?: boolean
|
||||
border?: string
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
nativeType?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
9
src/module/layer/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { App } from 'vue'
|
||||
import Component from './index.vue'
|
||||
import type { IDefineComponent } from '../type/index'
|
||||
|
||||
Component.install = (app: App) => {
|
||||
app.component(Component.name || 'LayLayer', Component)
|
||||
}
|
||||
|
||||
export default Component as IDefineComponent
|
||||
159
src/module/layer/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="visible"
|
||||
:id="id"
|
||||
class="layui-layer"
|
||||
style="z-index: 19891021; position: fixed"
|
||||
:style="{
|
||||
top: top,
|
||||
left: left,
|
||||
width: width,
|
||||
height: height,
|
||||
}"
|
||||
>
|
||||
<div class="layui-layer-title" style="cursor: move">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div class="layui-layer-content">
|
||||
<div :style="{ height: contentHeight }" v-if="type === 1">
|
||||
<slot v-if="slot.default"></slot>
|
||||
<template v-else>
|
||||
{{ content }}
|
||||
</template>
|
||||
</div>
|
||||
<iframe
|
||||
v-if="type === 2"
|
||||
scrolling="auto"
|
||||
allowtransparency="true"
|
||||
frameborder="0"
|
||||
:src="content"
|
||||
style="width: 100%"
|
||||
:style="{ height: contentHeight }"
|
||||
></iframe>
|
||||
</div>
|
||||
<span class="layui-layer-setwin"
|
||||
><a
|
||||
v-if="maxmin"
|
||||
class="layui-layer-min"
|
||||
href="javascript:;"
|
||||
@click="minHandle"
|
||||
><cite></cite></a
|
||||
><a
|
||||
class="layui-layer-ico layui-layer-max"
|
||||
:class="[max ? 'layui-layer-maxmin' : '']"
|
||||
href="javascript:;"
|
||||
v-if="maxmin"
|
||||
@click="maxHandle"
|
||||
></a>
|
||||
<a
|
||||
class="layui-layer-ico layui-layer-close layui-layer-close1"
|
||||
href="javascript:;"
|
||||
@click="closeHandle"
|
||||
></a
|
||||
></span>
|
||||
<div class="layui-layer-btn" v-if="btn && btn.length > 0">
|
||||
<template v-for="(b, index) in btn" :key="index">
|
||||
<a :class="['layui-layer-btn' + index]" @click="b.callback">{{
|
||||
b.text
|
||||
}}</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LayLayer',
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUpdated, ref, useSlots } from 'vue'
|
||||
import useMove from '../../hooks/useMove'
|
||||
|
||||
const slot = useSlots()
|
||||
|
||||
onMounted(() => {
|
||||
if (props.move) {
|
||||
const el = document.getElementById(props.id)
|
||||
if (el != null) {
|
||||
useMove(el)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
if (props.move) {
|
||||
const el = document.getElementById(props.id)
|
||||
if (el != null) {
|
||||
useMove(el)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
id?: string
|
||||
title?: string
|
||||
offset?: string[]
|
||||
width?: string
|
||||
height?: string
|
||||
visible?: boolean
|
||||
maxmin?: boolean
|
||||
btn: Record<string, unknown>[]
|
||||
move?: boolean
|
||||
type?: number
|
||||
content?: string
|
||||
}>(),
|
||||
{
|
||||
id: 'layer',
|
||||
title: '标题',
|
||||
offset: () => ['50%', '50%'],
|
||||
width: '390px',
|
||||
height: '330px',
|
||||
visible: true,
|
||||
maxmin: false,
|
||||
move: false,
|
||||
type: 1,
|
||||
btn: () => [],
|
||||
}
|
||||
)
|
||||
|
||||
const top = ref(props.offset[0])
|
||||
const left = ref(props.offset[1])
|
||||
const width = ref(props.width)
|
||||
const height = ref(props.height)
|
||||
const max = ref(false)
|
||||
const contentHeight = ref(
|
||||
props.btn.length > 0
|
||||
? 'calc(' + props.height + ' - 100px)'
|
||||
: 'calc(' + props.height + ' - 50px)'
|
||||
)
|
||||
|
||||
const emit = defineEmits(['close', 'update:visible'])
|
||||
|
||||
const closeHandle = function () {
|
||||
emit('close')
|
||||
emit('update:visible', false)
|
||||
}
|
||||
|
||||
const minHandle = function () {
|
||||
emit('close')
|
||||
emit('update:visible', false)
|
||||
}
|
||||
|
||||
const maxHandle = function () {
|
||||
if (max.value) {
|
||||
width.value = props.width
|
||||
height.value = props.height
|
||||
top.value = props.offset[0]
|
||||
left.value = props.offset[1]
|
||||
} else {
|
||||
width.value = '100%'
|
||||
height.value = '100%'
|
||||
top.value = '0px'
|
||||
left.value = '0px'
|
||||
}
|
||||
max.value = !max.value
|
||||
}
|
||||
</script>
|
||||
@@ -6,7 +6,9 @@
|
||||
:disabled="disabled"
|
||||
class="layui-textarea"
|
||||
:class="{ 'layui-disabled': disabled }"
|
||||
@input="updateValue"
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -20,11 +22,19 @@ const props = defineProps<{
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'input'])
|
||||
const emit = defineEmits(['update:modelValue', 'input', 'focus', 'blur'])
|
||||
|
||||
const updateValue = function (event: InputEvent) {
|
||||
const onInput = function (event: InputEvent) {
|
||||
const inputElement = event.target as HTMLInputElement
|
||||
emit('update:modelValue', inputElement.value)
|
||||
emit('input', inputElement.value)
|
||||
}
|
||||
|
||||
const onFocus = function (event: FocusEvent) {
|
||||
emit('focus', event)
|
||||
}
|
||||
|
||||
const onBlur = function () {
|
||||
emit('blur')
|
||||
}
|
||||
</script>
|
||||
|
||||