文章目录
1、this.$emit
this.$emit 是 Vue.js 中一个非常重要的方法,它允许子组件向父组件发送自定义事件,并传递数据。
基本用法:
-
子组件
this.$emit('eventName', data);
-
父组件
注意 eventName 和 子组件声明的一致,父组件函数名的定义有一个入参,但是只需写一个函数名<TagsMenu @eventName="父组件函数名" />
<script> import TagsMenu from './TagsMenu.vue'; export default { components: { TagsMenu }, methods: { 父组件函数名(data) { alert(data); },
eventName: 你要发送的自定义事件的名称。
data: 你要传递给父组件的数据,可以是任何类型,例如字符串、数字、对象、数组等。
2、简单举例
2.1、子组件
<ul>
<li v-for="item,index in tmenu"
:key="index"
v-show="isShowLi(index)"
@click="hdClick(index)"
>
</li>
</ul>
</div>
</template>
<script>
export default {
methods: {
hdClick(index){
this.$emit("fn",index)
}
2.2、父组件
父组件需要使用一个函数接受子组件传递来的参数
<TagsMenu @fn="clickMenu" />
</div>
</template>
<script>
import TagsMenu from './TagsMenu.vue';
export default {
components: {
TagsMenu
},
data() {
return {
tags: [{
title: "layout",
path: "/layout",
isActive: true,
}],
isShowTagsMenu: false,
clientX: 0,
clientY: 0,
clickIndex: 0,
}
},
mounted() {
document.addEventListener("click", this.closeMenu)
},
beforeDestroy() {
document.removeEventListener("click", this.closeMenu)
},
methods: {
clickMenu(i) {
if (i == 5) {
//关闭全部
this.tags = [{
"title": "layout",
"path": "/layout"
}]
this.goTo("/layout")
}
},
3、完整例子
3.1、子组件
这里,调用子组件 hdClick 方法时,将 index 函数传递给父组件,自定义变量为 fn
<template>
<div class="tags-menu" :style="{left:clientX+'px',top:clientY+'px'}">
<ul>
<li v-for="item,index in tmenu"
:key="index"
v-show="isShowLi(index)"
@click="hdClick(index)"
>
<i :class="item.icon"></i>
{{item.text}}
</li>
<li>{{clientX+","+clickIndex}}</li>
</ul>
</div>
</template>
<script>
export default {
props: ["clientX", "clientY", "clickIndex","tagsLength"],
methods: {
hdClick(index){
this.$emit("fn",index)
}
,
isShowLi(i) {
if(this.tagsLength===1){
//只有首页
return i===0;
}
if (this.clickIndex == 0) {
return ![1, 3].includes(i)
}
if(this.clickIndex == 1 && this.clickIndex==this.tagsLength-1){
return ![3,4].includes(i)
}else if(this.clickIndex == 1 && this.clickIndex!=this.tagsLength-1){
return ![3].includes(i)
}else if(this.clickIndex==this.tagsLength-1){
return ![4].includes(i)
}
return true;
}
},
data() {
return {
tmenu: [{
icon: "el-icon-refresh-right",
text: "刷新页面"
},
{
icon: "el-icon-close",
text: "关闭当前"
},
{
icon: "el-icon-circle-close",
text: "关闭其他"
},
{
icon: "el-icon-back",
text: "关闭左侧"
},
{
icon: "el-icon-right",
text: "关闭右侧"
},
{
icon: "el-icon-circle-close",
text: "关闭全部"
}
]
}
}
}
</script>
<style>
.tags-menu {
position: absolute;
z-index: 1000;
/* 确保菜单在最上层 */
background-color: white;
/* 设置背景颜色为白色 */
border: 1px solid #ddd;
/* 添加边框 */
border-radius: 4px;
/* 圆角效果 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
/* 添加阴影 */
padding: 10px;
/* 内边距 */
min-width: 120px;
/* 最小宽度 */
/* 可选:添加过渡效果 */
transition: opacity 0.2s ease;
}
.tags-menu ul {
list-style: none;
/* 去掉列表样式 */
margin: 0;
/* 去掉外边距 */
padding: 0;
/* 去掉内边距 */
}
.tags-menu li {
padding: 8px 12px;
/* 单个菜单项的内边距 */
cursor: pointer;
/* 鼠标悬停变成手指 */
transition: background-color 0.2s;
/* 添加过渡效果 */
}
.tags-menu li:hover {
background-color: #f5f5f5;
/* 悬停时的背景颜色 */
}
</style>
3.2、父组件
这里父组件通过 @fn="clickMenu" 获得子组件的上传的参数,然后交给 clickMenu(i) 函数处理
<template>
<div class="tags">
<!-- .native 用法:在 Vue.js 中,.native 修饰符用于监听原生 DOM 事件,而不是 Vue 组件的自定义事件。
例如,@contextmenu.native="rightClick($event)"
.prevent 阻止浏览器默认行为
表示你希望监听原生的 contextmenu 事件(通常是右键点击),
而不是 Vue 组件中可能定义的 contextmenu 事件。这在你想要直接处理 DOM 事件时非常有用,
尤其是当组件内部没有提供相应的事件时。 -->
<el-tag size="medium" :closable="index>0" v-for="item,index in tags" :key="item.path"
:effect="item.title==$route.name?'dark':'plain'" @click="goTo(item.path)" @close="close(index)"
:disable-transitions="true" @contextmenu.native.prevent="rightClick($event,index)">
<i class="cir" v-show="item.title==$route.name"></i>
{{item.title}}</el-tag>
<TagsMenu v-show="isShowTagsMenu" :clientX="clientX" :clientY="clientY" :clickIndex="clickIndex"
:tagsLength="tags.length" @fn="clickMenu" />
</div>
</template>
<script>
import TagsMenu from './TagsMenu.vue';
export default {
components: {
TagsMenu
},
data() {
return {
tags: [{
title: "layout",
path: "/layout",
isActive: true,
}],
isShowTagsMenu: false,
clientX: 0,
clientY: 0,
clickIndex: 0,
}
},
mounted() {
document.addEventListener("click", this.closeMenu)
},
beforeDestroy() {
document.removeEventListener("click", this.closeMenu)
},
methods: {
clickMenu(i) {
if (i == 5) {
//关闭全部
this.tags = [{
"title": "layout",
"path": "/layout"
}]
this.goTo("/layout")
}
},
closeMenu() {
this.isShowTagsMenu = false
},
rightClick(e, i) {
console.log("右键点击了", e.clientX, e.clientY, i);
this.clientX = e.clientX;
this.clientY = e.clientY;
this.isShowTagsMenu = true;
this.clickIndex = i;
//关闭浏览器的默认行为
window.event.returnValue = false;
return false;
},
goTo(path) {
this.$router.replace({
path: (path == '/' || path == undefined ? '/Index' : path)
});
},
close(index) {
const name = this.tags[index].title;
this.tags.splice(index, 1);
if (this.tags.length == 0) return;
//如果关闭当前页,则激活最后一个标签页
const path = this.tags[this.tags.length - 1].path;
if (name === this.$route.name && this.tags.length != 0) {
this.$router.replace({
path: (path == '/' || path == undefined ? '/Index' : path)
});
}
}
},
watch: {
$route: {
immediate: true,
handler(val, oldVal) {
console.log(val);
const bool = this.tags.find(item => {
return item.path == val.path;
});
if (!bool) {
this.tags.push({
title: val.name,
path: val.path
});
}
}
}
}
}
</script>
<style scoped>
.tags {
margin-top: 3px;
/* 添加距离上边缘的距离 */
}
.tags .el-tag {
padding-left: 10px;
padding-top: 0px;
margin-right: 5px;
.cir {
width: 8px;
height: 8px;
margin-right: 4px;
background-color: #fff;
border-radius: 50%;
display: inline-block;
}
}
</style>