ivew-adminルーティングおよびナビゲーションコンポーネント(新しいブラウザーウィンドウの効果と同様)パッケージプロジェクトの戦闘

RT :(サポートは、複数のページでルーティング、左のリンクにスクロールメニューへの切り替えの影響について:自己構成されたプロジェクトの要件に応じて)はここに画像の説明を挿入
、コードにあまり話をしなかった--------------- I美しい分割線 *** ---------------------:
変更
が必要なファイルファイル1.ツールに必要なメソッドはすべてここにあります
ここに画像の説明を挿入
ファイル2:ルートナビゲーションファイルの
ここに画像の説明を挿入
ファイル3:入り口ページ参照(マルチプロジェクトとの互換性のために、参照ここではオンデマンドの変化に使用している場合、パッケージの契約を行うには)
以下は、各ファイルのコードである
ファイル1:

import { objEqual } from "@/libs/tools";
import { setStore, getStore } from '@/libs/utils/storeage';
export let routeNav = {


  /**
 * 判断打开的标签列表里是否已存在这个新添加的路由对象
 */
  routeHasExist (tagNavList, routeItem) {
    let len = tagNavList.length
    let res = false
    this.doCustomTimes(len, (index) => {
      if (this.routeEqual(tagNavList[index], routeItem)) res = true
    })
    return res
  },
  /**
 * @param {Number} times 回调函数需要执行的次数
 * @param {Function} callback 回调函数
 */
  doCustomTimes (times, callback) {
    let i = -1
    while (++i < times) {
      callback(i)
    }
  },
  /**
   * 新增/关闭后重新赋值
   */
  setTagNavList (name, homeName, router, ) {
    // 空值处理
    let tagList = [];
    if (router) { tagList = [...router]; } else { tagList = JSON.parse(getStore(name)) || []; }
    if (tagList[0] && tagList[0].name !== homeName) { tagList.shift(); } // 如果第一个不是 homeName 删除
    let homeTagIndex = tagList.findIndex(item => item.name === homeName); // homeName判断
    if (homeTagIndex > 0) {
      let homeTag = tagList.splice(homeTagIndex, 1)[0];
      tagList.unshift(homeTag);
    }
    this.setTagNavListInLocalstorage(name, [...tagList]);
  },
  /**
  * @description 本地存储路由导航集合
 */
  setTagNavListInLocalstorage (name, list) {
    setStore(name, JSON.stringify(list))
  },
  getRouteTitleHandled (route) {
    let router = { ...route };
    let meta = { ...route.meta };
    let title = "";
    if (meta.title) {
      if (typeof meta.title === "function") title = meta.title(router);
      else title = meta.title;
    }
    meta.title = title;
    router.meta = meta;
    return router;
  },
  showTitle (item, vm) {
    let title = item.meta.title;
    if (!title) return;
    return title;
  },
  /**
 * @description 根据name/params/query判断两个路由对象是否相等
 * @param {*} route1 路由对象
 * @param {*} route2 路由对象
 */
  routeEqual (route1, route2) {
    const params1 = route1.params || {};
    const params2 = route2.params || {};
    const query1 = route1.query || {};
    const query2 = route2.query || {};
    return (route1.name === route2.name && objEqual(params1, params2) && objEqual(query1, query2));
  },
  /**
 * @param {*} list 现有标签导航列表
 * @param {*} newRoute 新添加的路由原信息对象
 * @description 如果该newRoute已经存在则不再添加
 */
  getNewTagList (list, newRoute) {
    const { name, path, meta, query, params } = newRoute;
    let newList = [...list];
    // if (this.routeEqual(tagNavList[index], routeItem)) res = true
    if (newList.findIndex(item => this.routeEqual(item, newRoute)) >= 0) { return newList; } else {
      newList.push({ name, path, meta, query, params });
    }
    return newList;
  },
  /**
 * @param {Array} list 标签列表
 * @param {String} name 当前关闭的标签的name
 */
  getNextRoute (homeName, list, route) {
    let res = {};
    if (list.length === 2) {
      res = this.getHomeRoute(homeName, list);
    } else {
      const index = list.findIndex(item => this.routeEqual(item, route));
      if (index === list.length - 1) { res = list[list.length - 2]; } else { res = list[index + 1]; }
    }
    return res;
  },
  /**
 * @param {Array} routers 路由列表数组
 * @description 用于找到路由列表中name为home的对象
 */
  getHomeRoute (homeName, routers) {
    let i = -1;
    let len = routers.length;
    let homeRoute = {};
    while (++i < len) {
      let item = routers[i];
      if (item.children && item.children.length) {
        let res = getHomeRoute(homeName, item.children);
        if (res.name) return res;
      } else {
        if (item.name === homeName) homeRoute = item;
      }
    }
    return homeRoute;
  },

};


objEqual 方法

/**
 * @param {*} obj1 对象
 * @param {*} obj2 对象
 * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
 */
export const objEqual = (obj1, obj2) => {
    const keysArr1 = Object.keys(obj1);
    const keysArr2 = Object.keys(obj2);
    if (keysArr1.length !== keysArr2.length) return false;
    else if (keysArr1.length === 0 && keysArr2.length === 0) return true;
    /* eslint-disable-next-line */ else
        return !keysArr1.some(key => obj1[key] != obj2[key]);
};

ファイル2:コンポーネントコード(2つの入力パラメーターlocStorageRouteNameを受け取ります:ローカルストレージ名homeName:デフォルトでルーティングを開きます)

<template>
    <div class="tags-nav">
        <!-- 我是路由导航 -->
        <div class="close-con">
            <Dropdown transfer @on-click="handleTagsOption" style="margin-top:6px;">
                <Button size="small" type="text">
                    <Icon :size="18" type="ios-close-circle-outline" />
                </Button>
                <DropdownMenu slot="list">
                    <DropdownItem name="close-all">关闭所有</DropdownItem>
                    <DropdownItem name="close-others">关闭其他</DropdownItem>
                </DropdownMenu>
            </Dropdown>
        </div>
        <div>
            <div class="btn-con left-btn">
                <Button type="text" @click="handleScroll(240)">
                    <Icon :size="18" type="ios-arrow-dropleft" />
                </Button>
            </div>
            <div class="btn-con right-btn">
                <Button type="text" @click="handleScroll(-240)">
                    <Icon :size="18" type="ios-arrow-dropright" />
                </Button>
            </div>
            <div class="scroll-outer" ref="scrollOuter" @DOMMouseScroll="handlescroll" @mousewheel="handlescroll">
                <div ref="scrollBody" class="scroll-body" :style="{left: tagBodyLeft + 'px'}">
                    <transition-group name="taglist-moving-animation">
                        <Tag type="dot" ref="tagsPageOpened" :closable="item.name !== homeName" v-for="(item, index) in routerList" :color="isCurrentTag(item) ? 'success' : 'default'" :key="`tag-nav-${index}`" :name="item.name" :data-route-item="item" @on-close="close(item)" @click.native="handleClick(item)" @contextmenu.prevent.native="contextMenu(item, $event)">
                            {{ showTitleInside(item) }}
                        </Tag>
                    </transition-group>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import { getStore } from "@/libs/utils/storeage";
export default {
    name: "router-navigation",
    props: {
        homeName: {
            type: String,
            default () {
                return ''
            }
        },
        homeRoute: {
            type: Object,
            default () {
                return {}
            }
        },
        locStorageRouteName: {
            type: String,
            default () {
                return ''
            }
        }
    },
    data () {
        return {
            routerList: [],
            tagBodyLeft: 0,
            rightOffset: 40,
            outerPadding: 4,
            contextMenuLeft: 0,
            contextMenuTop: 0,
            visible: false,
        };
    },
    computed: {
        currentRouteObj () {
            const { name, params, query } = this.$route;
            return { name, params, query };
        }
    },
    watch: {
        $route (newRoute, old) {
            this.getTagElementByName(newRoute);
            const { name, query, params, meta } = newRoute;
            this.setRouteTag(this.$Utils.routeNav.getNewTagList(this.routerList, newRoute))
            this.$emit('updateOpenName', newRoute.name);
        },
        visible (value) {
            if (value) {
                document.body.addEventListener("click", this.closeMenu());
            } else {
                document.body.removeEventListener("click", this.closeMenu());
            }
        }
    },
    mounted () {
        this.setRouterList()
        const { name, query, params, meta } = this.$route;
        this.setRouteTag(this.$Utils.routeNav.getNewTagList(this.routerList, this.homeRoute)) // 初始化的时候默认把homeRoute加入路由导航(ps:兼容跨项目跳转子页面)
        this.$nextTick(() => {
            this.setRouteTag(this.$Utils.routeNav.getNewTagList(this.routerList, this.$route))
        })
        this.getTagElementByName(this.$route);
    },
    methods: {
        setRouterList () { // 获取本地路由路径
            this.routerList = JSON.parse(getStore(this.locStorageRouteName)) || [];
        },
        setRouteTag (route) {
            this.$Utils.routeNav.setTagNavList(this.locStorageRouteName, this.homeName, route || this.$route);
            this.setRouterList()
        },
        handleCloseTag (res, type, route) { // 关闭标签
            if (type === "all") {
                this.handleClick(this.homeName);
            } else if (this.$Utils.routeNav.routeEqual(this.$route, route)) {
                if (type !== "others") {
                    const nextRoute = this.$Utils.routeNav.getNextRoute(this.homeName, this.routerList, route);
                    this.$router.push(nextRoute);
                }
            }
            this.setRouteTag(res)

        },
        // 是否选中
        isCurrentTag (item) {
            return this.$Utils.routeNav.routeEqual(this.currentRouteObj, item);
        },
        showTitleInside (item) { // 显示的title
            return this.$Utils.routeNav.showTitle(item, this);
        },
        contextMenu (item, e) { //contextmenu事件控制右键菜单
            if (item.name === this.homeName) { return; }
            this.visible = true;
            const offsetLeft = this.$el.getBoundingClientRect().left;
            this.contextMenuLeft = e.clientX - offsetLeft + 10;
            this.contextMenuTop = e.clientY - 64;
        },
        // 移动事件
        handlescroll (e) {
            var type = e.type;
            let delta = 0;
            if (type === "DOMMouseScroll" || type === "mousewheel") {
                delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40;
            }
            this.handleScroll(delta);
        },
        handleScroll (offset) {
            const outerWidth = this.$refs.scrollOuter.offsetWidth;
            const bodyWidth = this.$refs.scrollBody.offsetWidth;
            if (offset > 0) {
                this.tagBodyLeft = Math.min(0, this.tagBodyLeft + offset);
            } else {
                if (outerWidth < bodyWidth) {
                    if (this.tagBodyLeft < -(bodyWidth - outerWidth)) {
                        this.tagBodyLeft = this.tagBodyLeft;
                    } else {
                        this.tagBodyLeft = Math.max(this.tagBodyLeft + offset, outerWidth - bodyWidth);
                    }
                } else {
                    this.tagBodyLeft = 0;
                }
            }
        },
        handleTagsOption (type) { // 关闭按钮事件
            if (type.includes("all")) { // 关闭所有,除了home
                let res = this.routerList.filter(item => item.name === this.homeName);
                this.handleCloseTag(res, "all")
            } else if (type.includes("others")) {// 关闭除当前页和home页的其他页
                let res = this.routerList.filter(
                    item => this.$Utils.routeNav.routeEqual(this.currentRouteObj, item) || item.name === this.homeName
                );
                this.handleCloseTag(res, "others", this.currentRouteObj)
                setTimeout(() => { this.getTagElementByName(this.currentRouteObj.name); }, 100);
            }
        },
        getTagElementByName (route) {
            this.$nextTick(() => {
                this.refsTag = this.$refs.tagsPageOpened || [];
                this.refsTag.forEach((item, index) => {
                    if (this.$Utils.routeNav.routeEqual(route, item.$attrs["data-route-item"])) {
                        let tag = this.refsTag[index].$el;
                        this.moveToView(tag);
                    }
                });
            });
        },
        moveToView (tag) {// 移动
            const outerWidth = this.$refs.scrollOuter.offsetWidth;
            const bodyWidth = this.$refs.scrollBody.offsetWidth;
            if (bodyWidth < outerWidth) {
                this.tagBodyLeft = 0;
            } else if (tag.offsetLeft < -this.tagBodyLeft) {
                // 标签在可视区域左侧
                this.tagBodyLeft = -tag.offsetLeft + this.outerPadding;
            } else if (
                tag.offsetLeft > -this.tagBodyLeft && tag.offsetLeft + tag.offsetWidth < -this.tagBodyLeft + outerWidth
            ) {
                // 标签在可视区域
                this.tagBodyLeft = Math.min(0, outerWidth - tag.offsetWidth - tag.offsetLeft - this.outerPadding);
            } else {
                // 标签在可视区域右侧
                this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth));
            }
        },
        close (route) { // 关闭 列表内 某个 标签
            let res = this.routerList.filter(item => !this.$Utils.routeNav.routeEqual(route, item));
            this.handleCloseTag(res, undefined, route)
        },
        handleClick (route) { // 点击切换 列表内 某个 标签
            let { name, params, query } = {};
            if (typeof route === "string") { name = route; } else {
                name = route.name;
                params = route.params;
                query = route.query;
            }
            if (this.$Utils.routeNav.routeEqual(this.$route, route)) {
                console.log('请勿重复点击当前页面导航,如有需要请刷新当前页面!')
            } else {
                this.$router.push({ name, params, query });
            }
        },
        closeMenu () {
            this.visible = false;
        }
    },

};
</script>

<style lang="less">
@import "./index.less";
</style>


.no-select{
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.size{
  width: 100%;
  height: 100%;
}
.tags-nav{
  position: relative;
  width: 100%;
  border-top: 1px solid #F0F0F0;
  border-bottom: 1px solid #F0F0F0;
  .no-select;
  .size;
  .close-con{
    position: absolute;
    right: 0;
    top: 0;
    height: 100%;
    width: 34px;
    background: #fff;
    text-align: center;
    z-index: 10;
  }
  .btn-con{
    position: absolute;
    top: 0px;
    height: 100%;
    background: #fff;
    padding-top: 3px;
    z-index: 10;
    button{
      padding: 6px 4px;
      line-height: 14px;
      text-align: center;
    }
    &.left-btn{
      left: 0px;
    }
    &.right-btn{
      right: 32px;
      border-right: 1px solid #F0F0F0;
    }
  }
  .scroll-outer{
    position: absolute;
    left: 28px;
    right: 61px;
    top: 0;
    bottom: 0;
    box-shadow: 0px 0 3px 2px rgba(100,100,100,.1) inset;
    background: #F6F6F6;
    .scroll-body{
      height: ~"calc(100% - 1px)";
      display: inline-block;
      padding: 1px 4px 0;
      position: absolute;
      overflow: visible;
      white-space: nowrap;
      transition: left .3s ease;
      .ivu-tag-dot-inner{
        transition: background .2s ease;
      }
      .ivu-tag-default {
        background: #E8E8E8!important;
        font-size: 14px;
        color: #999999;
      }
    }
  }
  .contextmenu {
    position: absolute;
    margin: 0;
    padding: 5px 0;
    background: #fff;
    z-index: 100;
    list-style-type: none;
    border-radius: 4px;
    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
    li {
      margin: 0;
      padding: 5px 15px;
      cursor: pointer;
      &:hover {
        background: #eee;
      }
    }
  }
}

ファイル3:

<template>
    <div class="app">
        <frame-nav></frame-nav>
        <div class="float">
            <div class="float-left">
                <page-list ref="sideMenu"></page-list>
            </div>
            <div class="float-right">
                <div class="tag-nav-wrapper">
                    <tags-nav :locStorageRouteName="locStorageRouteName" :homeName="homeName" @updateOpenName="updateOpenName" />
                </div>
                <div v-if="reload">
                    <keep-alive :include="cacheList">
                        <router-view />
                    </keep-alive>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import frameNav from './components/frame-nav'
import pageList from './components/page-link-list'
import { mapState, mapActions } from "vuex";
import TagsNav from "../../../components/route-navigation";
import { getStore } from "@/libs/utils/storeage";
export default {
    name: "Layout",
    components: { frameNav, pageList, TagsNav }, //TagsNav
    computed: {
        cacheList () {
            return [
                ...(this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : [])
            ];
        }
    },
    watch: {
        $route (newRoute, old) {
            this.tagNavList = JSON.parse(getStore(this.locStorageRouteName)) || [];
            if (newRoute.name == old.name) {
                this.reload = false
                setTimeout(() => { this.reload = true }, 50)
            }
        },
    },
    data () {
        return {
            tagNavList: [],
            reload: true,
            locStorageRouteName: 'xxbDevOpsAdminRouteList',
            homeName: 'shopList',
        }
    },
    async created () { },
    async mounted () { },
    methods: {
        ...mapActions(["loginScheduler"]),
        updateOpenName (name) {
            this.$refs.sideMenu.updateOpenName(name);
        },
    }
}
</script>

<style scoped>
html,
body,
.app {
    position: relative;
    width: 100%;
    height: 100%;
}
.float {
    overflow: hidden;
    height: calc(100% - 40px);
}
.float-left {
    float: left;
    width: 200px;
    height: 100%;
}
.float-right {
    float: right;
    width: calc(100% - 200px);
    overflow-y: auto;
    overflow-x: hidden;
    height: 100%;
}
.tag-nav-wrapper {
    padding: 0;
    height: 40px;
    background: #f0f0f0;
}
</style>
89件のオリジナル記事を公開 103のように 130,000以上を訪問

おすすめ

転載: blog.csdn.net/qq_39517820/article/details/102717744