应用场景
假设我们有一个下拉框组件,当下拉框展开的时候,点击下拉框之外的元素可以自动关闭下拉框。
一 ClickOutside代码示例
在vue3中使用ClickOutside
// 导入自定义指令
import { ClickOutside as vClickOutside } from 'element-plus';
// 绑定指令触发对应事件
<div id="d1" v-click-outside="closeOrder">
暂无数据
</div>
// 写相关逻辑事件
const closeOrder = () => {
console.log('点击了d1区域之外的位置')
}
id="d1"记得写,否则不生效
vue2中使用ClickOutside
// 导入指令
import { ClickOutside } from 'element-plus';
// 映射该指令
directives:{ ClickOutside }
// 绑定指令触发对应事件
<div id="d1" v-clickOutside="closeOrder">
暂无数据
</div>
// 写相关逻辑事件
closeOrder(){
console.log('点击了d1区域之外的位置')
}
案例
<template>
<div ref="dropdownRef" class="drop_down" id="d1" v-click-outside="closeOrder">
<div class="cascade-input" @click="toggleDropdown" :class="{ 'is-active': isDropdownVisible }">
<span class="cascade_choose">请选择</span>
<el-icon :class="{ 'is-rotate': isDropdownVisible }" class="down"><ArrowDown /></el-icon>
</div>
<div class="cascade-dropdown" v-if="isDropdownVisible">
<div style="display: flex">
<!-- 一级菜单 -->
<ul>
<li
class="cascade_item"
v-for="(item, index) in cascadeType"
:key="index"
@click="chooseType(item.field)"
:class="{ active: chooseTypeIndex == item.field }"
>
<span class="cascade_item_name">{{ item.name }}</span
><span class="cascade_item_name">
<el-icon><ArrowRight /></el-icon>
</span>
</li>
</ul>
<!-- 二级菜单 -->
<div class="choose" v-show="isShowTwoMenu">
<div class="choose_header">
<ul>
<li
style="cursor: pointer"
v-for="(item, index) in MustList"
@click="changeMustTab(item.id)"
:key="index"
:class="{ 'is-must': isMust == item.id }"
>{{ item.name }}</li
>
</ul>
</div>
<div class="choose_content">
<ul>
<template v-for="item in secondList" :key="item.id">
<li
v-if="!item.subImportField"
style="cursor: pointer"
@click="changeItem(item.id)"
:class="{ 'is-choose': isChooseItem == item.id }"
>{{ item.name }}</li
>
<template v-if="item.subImportField?.length > 0">
<div class="links">以下联系方式至少选一种</div>
<li
v-for="i in item.subImportField"
:key="i.id"
style="cursor: pointer"
@click="changeItem(i.id)"
:class="{ 'is-choose': isChooseItem == i.id }"
>{{ i.name }}</li
>
</template>
</template>
</ul>
</div>
<div class="choose_footer"> 新增自定义字段 </div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ArrowDown, ArrowRight } from "@element-plus/icons-vue"
import { ClickOutside as vClickOutside } from "element-plus"
/**
* 切换
*/
const isDropdownVisible = ref(false)
const cascadeType = ref([
{
field: 1,
name: "客户"
},
{
field: 2,
name: "联系人"
},
{
field: 3,
name: "跟进记录"
}
])
const toggleDropdown = () => {
// debugger
isDropdownVisible.value = !isDropdownVisible.value
console.log("isDropdownVisible.value", isDropdownVisible.value)
if (!isDropdownVisible.value) {
isShowTwoMenu.value = false
chooseTypeIndex.value = null
}
}
const closeOrder = () => {
console.log("点击了d1区域之外的位置")
isDropdownVisible.value = false
}
// const dropdownRef = ref(null)
// const handleClickOutside = (event) => {
// if (dropdownRef.value && !dropdownRef.value.contains(event.target)) {
// isDropdownVisible.value = false
// }
// }
// onMounted(() => {
// debugger
// document.addEventListener("click", handleClickOutside)
// })
// onBeforeUnmount(() => {
// document.removeEventListener("click", handleClickOutside)
// })
//切换类型
const chooseTypeIndex = ref<any>(null)
//展示二级菜单
const isShowTwoMenu = ref(false)
const secondList = ref<any>([])
// const secondListByNoMust = ref<any>([])
const chooseType = (field: any) => {
chooseTypeIndex.value = field
isShowTwoMenu.value = true
isMust.value = 1
// 获取二级菜单数据
switch (field) {
case 1:
secondList.value = [
{
name: "称谓",
id: 111,
type: 0
},
{
name: "生日",
id: 22,
type: 0
},
{
name: "联系方式",
id: 23,
type: 1,
subImportField: [
{
name: "邮箱",
id: 24
},
{
name: "手机",
id: 25
}
]
}
]
break
case 2:
secondList.value = [
{
name: "传真",
id: 111,
type: 0
},
{
name: "职务",
id: 22,
type: 0
}
]
break
}
}
const isChooseItem = ref("")
const changeItem = (id: any) => {
isChooseItem.value = id
}
// 切换必须非必选
const isMust = ref(1)
const MustList = ref([
{
name: "必须",
id: 1
},
{
name: "非必须",
id: 2
}
])
const changeMustTab = (id) => {
isMust.value = id
isChooseItem.value = ""
switch (id) {
case 1:
secondList.value = [
{
name: "称谓",
id: 111,
type: 0
},
{
name: "生日",
id: 22,
type: 0
},
{
name: "联系方式",
id: 23,
type: 1,
subImportField: [
{
name: "邮箱",
id: 24
},
{
name: "手机",
id: 25
}
]
}
]
break
case 2:
secondList.value = [
{
name: "传真",
id: 111,
type: 0
},
{
name: "职务",
id: 22,
type: 0
}
]
break
}
}
</script>
<style scoped lang="scss">
.drop_down {
position: relative;
.cascade-input {
height: 40px;
padding: 0px 12px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
.cascade_choose {
color: #333333;
font-size: 16px;
font-weight: 400;
}
}
.cascade-input.is-active {
border-color: #409eff;
}
.down {
transition: all 0.4s;
}
.is-rotate {
transform: rotate(180deg);
}
.cascade-dropdown {
position: absolute;
top: 40px;
left: 0px;
z-index: 9999;
display: inline-block;
box-shadow: 0 2px 9px 0 #c8c9cc80;
height: 291px;
background: #fff;
border-radius: 2px;
border: 1px solid #ebedf0;
border-radius: 4px;
margin-top: 4px;
.active {
background: #f7f8fa;
}
.cascade_item {
width: 240px;
height: 36px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0px 12px;
cursor: pointer;
.cascade_item_name {
color: #323233;
font-size: 14px;
font-weight: 400;
}
}
.choose {
width: 240px;
height: 291px;
border-left: 1px solid #dcdfe6;
position: relative;
.choose_header {
width: 100%;
ul {
display: flex;
height: 36px;
line-height: 36px;
border-bottom: 1px solid #dedede;
box-sizing: border-box;
padding-left: 10px;
li {
margin-right: 20px;
color: #323233;
font-size: 14px;
font-weight: 500;
&:hover {
cursor: pointer;
}
}
}
.is-must {
color: #477fff;
border-bottom: 1px solid #477fff;
}
}
.choose_content {
// padding-left: 12px;
ul {
li {
height: 32px;
line-height: 32px;
padding-left: 12px;
}
.is-choose {
background: #f7f8fa;
}
}
.links {
color: #477fff;
font-size: 12px;
font-family: "苹方";
padding: 5px 0px 5px 12px;
}
}
.choose_footer {
position: absolute;
left: 0;
bottom: 0;
width: 92%;
height: 40px;
line-height: 40px;
text-align: center;
border-top: 1px solid #dcdfe6;
color: #477fff;
font-size: 14px;
font-weight: 400;
margin: 0px 10px;
}
}
}
}
</style>