以element ui 的标签页与侧边导航联动的vue功能

侧栏与标签页的联动效果在后台管理中很常用,这个是以element ui为基础写的标签页与侧边导航联动的项目部分。

在整体布局layout页面中,测栏,其中最重要的两个参数default-active设为$router.path,则它会根据路由来激活样式,index设为遍历数组的url,跟路由地址一一匹配。

思路是遍历导航栏数组,在数组中有标题,图标,路由地址等信息,与路由信息相对应,点击每个导航项目时会将该条数组项目添加到store里,删除则反之。这主要是一级路由的列表,那么其他页面的标签我是通过监听路由,在里面包不包含,不包含就从当前的路由元信息里取。

layout.vue文件

<template>
    <div class="layoutContent">
        <el-menu :default-active="$route.path" :router="true" active-text-color="#409eff" class="el-menu-vertical-demo"
            :class="{ showMenu: isCollapse }" mode="vertical">
            <!-- 因为要一直开着首页标签,所以我写死了 -->
            <el-menu-item @click="$router.push({ path: '/' })" index="/">
                <i class="el-icon-menu"></i>
                <span slot="title">首页</span>
            </el-menu-item>
            <el-menu-item v-for="(item, index) in list1" :key="item.title" :index="item.url"
                @select="menuSelect(index, indexPath)" @click="mainToPath(item)">
                <i :class="`el-icon-${item.icon}`"></i>
                <span slot="title">{{ item.title }}</span>
            </el-menu-item>
        </el-menu>
        <div class="layout">
            <div class="navbar">
                <i class="el-icon-s-unfold" @click="unfold"></i>
                <div class="user">
                    <i class="el-icon-refresh" style="font-size: 20px" @click="refresh()"></i>
                    <el-image style="width: 30px; height: 30px; border-radius: 50%"
                        src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"></el-image>

                    <el-row class="block-col-2">
                        <el-col :span="12">
                            <el-dropdown trigger="click">
                                <span class="el-dropdown-link">
                                    <i class="el-icon-arrow-down" style="font-size: 16px; margin-left: 10px"></i>
                                </span>
                                <el-dropdown-menu slot="dropdown">
                                    <el-dropdown-item v-for="(item, index) in list2" :icon="`el-icon-${item.icon}`"
                                        @click.native="setting(index)" :key="item.title">{{ item.title
                                        }}</el-dropdown-item>
                                </el-dropdown-menu>
                            </el-dropdown>
                        </el-col>
                    </el-row>
                </div>
            </div>
            <Head-Tag></Head-Tag>
            <el-dialog title="提示" :visible.sync="dialogVisible" width="90%">
                <span>确认退出吗</span>
                <span slot="footer" class="dialog-footer">
                    <el-button @click="dialogVisible = false" size="mini">取 消</el-button>
                    <el-button type="primary" size="mini" @click="dialogVisible = false">确 定</el-button>
                </span>
            </el-dialog>
            <router-view v-if="isRresh"></router-view>
        </div>
    </div>
</template>
<script>
    import { mapState, mapMutations } from "vuex";
    //tag组件
    import HeadTag from "@/components/headTag.vue";
    export default {
        components: {
            HeadTag,
        },
        data() {
            return {
                isCollapse: false,
                isRresh: true,
                dialogVisible: false,
                index: "",
                list1: [
                    { title: "", icon: "menu", url: "/***" },
                    { title: "", icon: "document", url: "/***" },
                    { title: "", icon: "setting", url: "" },
                ],
            };
        },

        watch: {
            ["$route.path"]: function (newVal, olderVal) {
                if (newVal === "/") return;
                if (newVal === "/editUser")
                    this.$route.meta.title = JSON.parse(
                        window.sessionStorage.getItem("title")
                    ).title;
                for (let j = 0; j < this.list1.length; j++) {
                    // 若上面列表里没有包含当前的路由信息,则从当前路由元信息里取
                    if (newVal && !this.list1[j].url.includes(newVal)) {
                        this.$store.commit("mutationSelectTags", this.$route.meta);
                    }
                }
            },
            ["$store.state.stateTagsList"]: function (newVal, olderVal) {
                // 若tag中的数据条为0,就跳转到首页
                if (newVal.length === 0) {
                    this.$router.push({ path: "/" }).catch((err) => { });
                }
            },
        },

        methods: {
            mainToPath(value) {
                // 一级主跳转并选择tag
                this.$store.commit("mutationSelectTags", value);
                this.$router.push({ path: value.url }).catch((err) => { });
            },
            unfold() {
                // 侧栏开关
                this.isCollapse = !this.isCollapse;
            },
            ...mapState({ state: "state" }),
            ...mapMutations({
                closeAll: "mutationCloseAllTag",
            }),
            setting(index) {
                //设置小功能,可有可无
                if (index === 0) {
                    // 关闭全部tag
                    this.closeAll();
                }
                if (index === 1) {
                    if (this.$router.history.current.path === "/") {
                        this.$message("您已在首页了!");
                        return;
                    }
                    this.$router.push({ path: "/" }).catch((err) => { });
                }
                if (index === 2) this.dialogVisible = true;
            },
        },
    };
</script>

<style>
    .el-tabs__content {
        display: none;
    }
</style>
<style lang="scss" scoped>
    .el-menu-item {
        padding-left: 1vh !important;
    }

    .layoutContent {
        display: flex;

        .el-menu-vertical-demo {
            width: 0px;
            overflow: hidden;
            transition: all 0.3s;
        }

        .showMenu {
            width: 118px;
        }
    }

    .layout {
        background-color: rgb(247, 248, 250);
        height: 100vh;
        width: 70%;
        // flex-shrink: 1;
        flex-grow: 1;

        .tabs {
            background-color: white;
            margin-top: 2px;
        }

        .navbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            width: 100%;
            height: 50px;
            background-color: white;
            font-size: 25px;
            padding: 0 20px;
            box-sizing: border-box;
        }

        .user {
            display: flex;
            align-items: center;
        }

        .el-icon-refresh {
            margin-right: 10px;
        }
    }

    .el-dropdown-link {
        cursor: pointer;
        color: #409eff;
    }

    .el-icon-arrow-down {
        font-size: 12px;
    }

    .demonstration {
        display: block;
        color: #8492a6;
        font-size: 14px;
        margin-bottom: 20px;
    }

    ::v-deep .el-tabs {
        background-color: #fff;
    }

    ::v-deep .el-tabs--card>.el-tabs__header .el-tabs__item {
        background-color: #fff;
        border-radius: 0px 0px 10px 10px !important;
    }

    ::v-deep .el-tabs--card>.el-tabs__header .el-tabs__nav,
    ::v-deep .el-tabs--card>.el-tabs__header {
        border: none !important;
        background-color: #eaf4fe !important;
    }

    ::v-deep .el-tabs--card>.el-tabs__header .el-tabs__item.is-active {
        border: none !important;
        border-radius: 10px 10px 0px 0px !important;
        background: transparent !important;
    }
</style>

tags.vue文件

<template>
    <div class="app-tag">
        <el-tag size="medium" :disable-transitions="true" :effect="$route.path === '/' ? 'dark' : 'plain'" :hit="true"
            @click="$router.push({ path: '/' })">
            <i class="el-icon-custom"></i>
            首页
        </el-tag>
        <el-tag closable size="medium" v-for="(tag, index) in tags" :key="index" :disable-transitions="true"
            :effect="$route.path === tag.url ? 'dark' : 'plain'" :hit="true" @close="handleClose(tag, index)"
            @click="handleClick(tag)">
            <i :class="`el-icon-${tag.icon}`"></i>
            {{ tag.title }}
        </el-tag>
    </div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
    data() {
        return {
            tags: [],
        }
    },
    created() {
        //stateTagsList的值默认为空数组
        this.tags = this.stateTagsList;
    },
    computed: {
        ...mapState(['stateTagsList'])
    },
    mounted() {
        // 返回时删除二级tags目录
        let _this = this
        window.addEventListener("popstate", function (e) {
            _this.closeCurrent()
        }, false);
    },
    methods: {
        ...mapMutations({
            close: 'mutationCloseTag',
            closeCurrent: 'mutationCloseCurrentTag'
        }),
        handleClose(tag, index) {
            this.close(tag) // 删除当前tag
            if (this.$router.path === tag?.url) { // 如果关闭的标签不是当前路由的话,不做路由跳转
                return
            } else {
                if (index === (this.tags.length - 1)) { // 关闭最后一个标签,则路由跳转至最后一个
                    this.$router.push({ path: this.tags[index]?.url })
                } else { // 路由跳转至下一个标签页
                    if (index === 0) {
                        this.$router.push({ path: this.tags[0]?.url })
                    } else {
                        this.$router.push({ path: this.tags[index - 1]?.url })
                    }
                }
            }
        },
        // 点击tags具体标签
        handleClick(tag) {
            this.$router.push({ path: tag?.url }).catch(err => { })
        }
    }
}
</script>
<style lang="scss" scoped>
.app-tag {
    padding: 0 10px;
    margin-bottom: 1vh;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    overflow-x: scroll;
    padding-bottom: 4px;

    .el-tag {
        cursor: pointer;
        border: 0;
        border-radius: 0 0 10px 10px;
        background-color: #fff;
        color: black;

        ::v-deep .el-tag__close {
            color: black;
        }
    }

    .el-tag--dark {
        border-radius: 0;
        background-color: #eaf4fe;
        color: #409eff;

        ::v-deep .el-tag__close {
            color: #409eff;
        }
    }
}</style>

store文件

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    stateTagsList: [],
  },
  getters: {},
  mutations: {
    mutationSelectTags(state, data) {
      // 选择tag标签
      let result = false;
      for (let i = 0; i < state.stateTagsList.length; i++) {
        if (state.stateTagsList[i].url === data.url) {
          return (result = true);
        }
      }
      result === false ? state.stateTagsList.push(data) : "";
    },
    mutationCloseTag(state, data, _router) {
      //  单击关闭tag标签
      let result = state.stateTagsList.findIndex(
        (item) => item.url === data.url
      );
      state.stateTagsList.splice(result, 1);
    },
    mutationCloseAllTag(state, data) {
      // 关闭全部tag
      state.stateTagsList.splice(0, state.stateTagsList.length);
    },
    mutationCloseCurrentTag(state, data) {
      // 返回时关闭一个tag
      state.stateTagsList.splice(-1, 1);
    },
  },
  actions: {},
  modules: {},
});

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值