<script setup lang="ts">
import { ElDialog, ElScrollbar, ElButton } from 'element-plus'
import { propTypes } from '@/utils/propTypes'
import { computed, useAttrs, ref, unref, useSlots, watch, nextTick } from 'vue'
import { isNumber } from '@/utils/is'
import { useI18n } from '@/hooks/web/useI18n'
import { debounce } from 'lodash-es'
const { t } = useI18n()
const slots = useSlots()
const props = defineProps({
modelValue: propTypes.bool.def(false),
title: propTypes.string.def('Dialog'),
fullscreen: propTypes.bool.def(true),
maxHeight: propTypes.oneOfType([String, Number]).def('500px')
})
const getBindValue = computed(() => {
const delArr: string[] = ['fullscreen', 'title', 'maxHeight']
const attrs = useAttrs()
const obj = { ...attrs, ...props }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
})
const isFullscreen = ref(false)
const toggleFull = () => {
isFullscreen.value = !unref(isFullscreen)
}
const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight)
watch(
() => isFullscreen.value,
async (val: boolean) => {
await nextTick()
if (val) {
const windowHeight = document.documentElement.offsetHeight
dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px`
} else {
dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight
}
},
{
immediate: true
}
)
const dialogStyle = computed(() => {
return {
height: unref(dialogHeight)
}
})
const emit = defineEmits(['on-close', 'on-save'])
const handleClose = () => {
emit('on-close')
}
/** focus: 防抖使用方法demo */
const handleSave = debounce(() => {
emit('on-save')
}, 500)
</script>
<template>
<ElDialog
v-bind="getBindValue"
:fullscreen="isFullscreen"
destroy-on-close
lock-scroll
draggable
:close-on-click-modal="false"
@close="handleClose"
>
<template #header>
<div class="flex justify-between">
<slot name="title">
{
{ title }}
</slot>
<Icon
v-if="fullscreen"
class="mr-18px cursor-pointer is-hover mt-2px"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
color="var(--el-color-info)"
@click="toggleFull"
/>
</div>
</template>
<ElScrollbar :style="dialogStyle">
<slot></slot>
</ElScrollbar>
<template #footer>
<slot name="footer" v-if="slots.footer"></slot>
<template v-else>
<ElButton type="primary" @click="handleSave">{
{ t('common.save') }}</ElButton>
<ElButton @click="handleClose">{
{ t('common.cancel') }}</ElButton>
</template>
</template>
</ElDialog>
</template>
<style lang="less">
.@{elNamespace}-dialog__header {
margin-right: 0 !important;
border-bottom: 1px solid var(--tags-view-border-color);
}
.@{elNamespace}-dialog__footer {
border-top: 1px solid var(--tags-view-border-color);
}
.is-hover {
&:hover {
color: var(--el-color-primary) !important;
}
}
.dark {
.@{elNamespace}-dialog__header {
border-bottom: 1px solid var(--el-border-color);
}
.@{elNamespace}-dialog__footer {
border-top: 1px solid var(--el-border-color);
}
}
</style>
防抖:
一、定义:
即防止抖动,防止用户操作的结果抖动。一段时间内,事件在我们规定的间隔 n 秒内多次执行,回调只会执行一次。 如果在这n秒内又被触发,则重新计时。
二、特点:
等待某种操作停止后,加以间隔进行操作。
- 持续触发不执行
- 不触发的一段时间之后再执行
三、应用场景:
登录注册等表单提交操作用户点击过快触发多次请求、富文本编辑器邮件等编辑内容实时保存等。
四、总结:
防抖避免了误把一次操作认为多次操作,限制了事件执行的上限,即停止触发后 n 秒才去执行。
节流
一、定义:
即控制流量。用于用户在与页面交互时控制事件发生的频率,一般场景是单位的时间或其它间隔内定时执行操作。一段时间内,事件在每次到达我们规定的间隔 n 秒时触发一次。
二、特点:
- 持续触发并不会执行多次
- 到一定时间 / 其它间隔 ( 如滑动的高度 )再去执行
三、应用场景:
- 埋点。商品搜索列表、商品橱窗等,用户滑动时 定时 / 定滑动的高度 发送埋点请求
- 运维系统查看应用运行日志时,每 n 秒刷新一次
四、总结:
即事件触发过程中每间隔 n 秒去执行。同样的场景可能还有 scroll
mousemove
等更加频繁触发的事件、浏览器进度条位置计算、input
动态搜索等。
参考文章:防抖节流场景及应用 - 掘金