(十).netcore+vue vue-cli@4+element-ui+router+vuex

本章目的:UI整体框架搭起来 1、安装并引用element-ui需注意,vue-cli@4+的版本,在创建项目时,选择vue2的版本,如果选择vue3的版本就不能这样引入element-ui了npm i element-ui -Smain.js 引入element-uiimport...

本章目的:UI整体框架搭起来

 

1、安装并引用element-ui

需注意,vue-cli@4+的版本,在创建项目时,选择vue2的版本,如果选择vue3的版本就不能这样引入element-ui了

npm i element-ui -S

main.js 引入element-ui

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

2、router/index.js 设置路由

菜单先写死,然后根据菜单集合动态配置路由

 

import Vue from 'vue'
import Router from 'vue-router'
import Home from '../views/Home.vue'
//import User from '../views/User/Users.vue'
//import Role from '../views/User/Roles.vue'
//import Menu from '../views/Permission/Menu.vue'
//import RoleMenu from '../views/Permission/RoleMenu.vue'
//import Action from '../views/Permission/Action.vue'
import Layout from "../views/Layout/Layout";
const _import = require('@/router/_importview.js')//获取组件的方法
Vue.use(Router)

//菜单集合
const routesList = [
    {
        id: "1",
        path: '/',
        name: '首页',
        iconCls: 'el-icon-s-home',
        IsHide: false,
        meta: {
            title: '首页'
        }
    },
    {
        id: "2",
        path: '-',
        name: '用户角色管理',
        iconCls: 'el-icon-user-solid',
        IsHide: false,
        children: [
            {
                id: "3",
                path: '/User/Users',
                name: '用户管理',
                IsHide: false,
                meta: {
                    title: '用户管理'
                }
            },
            {
                id: "4",
                path: '/User/Roles',
                name: '角色管理',
                IsHide: false,
                meta: {
                    title: '角色管理'
                }
            }
        ]
    },
    {
        id: "5",
        path: '-',
        name: '授权管理',
        iconCls: 'el-icon-menu',
        IsHide: false,
        children: [
            {
                id: "6",
                path: '/Permission/Menu',
                name: '菜单管理',
                IsHide: false,
                meta: {
                    title: '菜单管理'
                }
            },
            {
                id: "7",
                path: '/Permission/Action',
                name: '接口管理',
                IsHide: false,
                meta: {
                    title: '接口管理'
                }
            },
            {
                id: "8",
                path: '/Permission/RoleMenu',
                name: '菜单授权',
                IsHide: false,
                meta: {
                    title: '菜单授权'
                }
            }
        ]
    }
]

const createRouter = () => new Router({
    mode: 'history',
    routes: [{
        id: "1",
        path: '/',
        name: '首页',
        component: Home,
        iconCls: 'el-icon-s-home',
        IsHide: false,
        meta: {
            title: '首页'
        }
    }]
})

const router = createRouter()

/*--------------------------根据菜单集合动态配置路由----------------------*/
export function filterAsyncRouter(asyncRouterMap) {
    const accessedRouters = asyncRouterMap.filter(route => {
        if (route.path) {
            if (route.path === '-') {//一级菜单
                route.component = Layout
            } else {
                try {
                    route.component = _import(route.path.replace('/:id', ''))
                } catch (e) {
                    try {
                        route.component = () => import('@/views' + route.path.replace('/:id', '') + '.vue');
                    } catch (error) {
                    }
                }
            }
        }
        if (route.children && route.children.length) {
            route.children = filterAsyncRouter(route.children)
        }
        return true
    })
    return accessedRouters
}

router.$addRoutes = (params) => {
    var f = item => {
        if (item['children']) {
            item['children'] = item['children'].filter(f);
            return true;
        }else {
            return true;
        }
    }    
    var params = params.filter(f);
    router.addRoutes(params)
}

let getRouter = filterAsyncRouter(routesList); //过滤路由
router.$addRoutes(getRouter) //动态添加路由
/*--------------------------根据菜单集合动态配置路由----------------------*/

window.localStorage.router = (JSON.stringify(routesList));
export default router
View Code

 

3、模板页 App.vue

<template>
    <div id="app">
        <transition v-if="!$route.meta.NoNeedHome" name="fade" mode="out-in">
            <el-row class="container">
                <el-col :span="24" class="header">
                    <el-col :span="10" class="logo collapsedLogo" :class="collapsed?'logo-collapse-width':'logo-width'">
                        <div @click="toindex"> {{collapsed?sysNameShort:sysName}}</div>
                    </el-col>
                    <el-col :span="10" class="logoban">
                        <div :class=" collapsed?'tools collapsed':'tools'" @click="collapse">
                            <i class="fa el-icon-s-operation"></i>

                        </div>
                        <el-breadcrumb separator="/" class="breadcrumb-inner collapsedLogo">
                            <el-breadcrumb-item v-for="item in $route.matched" :key="item.path">
                                <span style=""> {{ item.name }}</span>
                            </el-breadcrumb-item>
                        </el-breadcrumb>
                    </el-col>
                    <el-col :span="4" class="userinfo">
                        <el-dropdown trigger="hover">
                            <span class="el-dropdown-link userinfo-inner">
                                {{sysUserName}}
                                <img src="./assets/logo.png" height="128" width="128" />
                            </span>
                            <el-dropdown-menu slot="dropdown">
                                <el-dropdown-item divided @click.native="logout">退出登录</el-dropdown-item>
                            </el-dropdown-menu>
                        </el-dropdown>
                    </el-col>
                </el-col>
                <el-col :span="24" class="main">
                    <aside :class="collapsedClass ">
                        <el-scrollbar style="height:100%;background: #2f3e52;" class="scrollbar-handle">
                            <el-menu :default-active="$route.path"
                                     class="el-menu-vertical-demo"
                                     unique-opened router :collapse="isCollapse"
                                     background-color="#2f3e52"
                                     style="border-right: none;"
                                     text-color="#fff"
                                     active-text-color="#ffd04b">
                                <sidebar v-for="(menu,index) in routes" @collaFa="collapseFa" :key="index"
                                         :item="menu" />
                            </el-menu>
                        </el-scrollbar>
                    </aside>
                    <el-col :span="24" class="content-wrapper"
                            :class="collapsed?'content-collapsed':'content-expanded'">
                        <div class="tags" v-if="showTags">

                            <div id="tags-view-container" class="tags-view-container">
                                <scroll-pane ref="scrollPane" class="tags-view-wrapper">
                                    <router-link v-for="(tag,index) in tagsList"
                                                 ref="tag"
                                                 :key="tag.path"
                                                 :class="{'active': isActive(tag.path)}"
                                                 :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
                                                 tag="span"
                                                 @click.middle.native="closeTags(index)"
                                                 class="tags-view-item">
                                        {{ tag.title }}
                                        <span class="el-icon-close" @click.prevent.stop="closeTags(index)" />
                                    </router-link>
                                </scroll-pane>
                            </div>
                            <!-- 其他操作按钮 -->
                            <div class="tags-close-box">
                                <el-dropdown @command="handleTags">
                                    <el-button size="mini">
                                        <i class="el-icon-arrow-down el-icon--right"></i>
                                    </el-button>
                                    <el-dropdown-menu size="small" slot="dropdown">
                                        <el-dropdown-item command="other">关闭其他</el-dropdown-item>
                                        <el-dropdown-item command="all">关闭所有</el-dropdown-item>
                                    </el-dropdown-menu>
                                </el-dropdown>
                            </div>
                        </div>
                        <transition name="fade" mode="out-in">
                            <div class="content-az router-view-withly">
                                <!-- 含有母版的路由页面 -->
                                <router-view></router-view>
                            </div>
                        </transition>
                    </el-col>

                </el-col>
            </el-row>

        </transition>
        <transition v-else name="fade" mode="out-in">
            <div class="content-az router-view-noly">
                <!-- 单独的页面 -->
                <router-view></router-view>
            </div>
        </transition>


        <div class="v-modal " @click="collapse" v-show="SidebarVisible" tabindex="0" style="z-index: 2917;"></div>

    </div>
</template>

<style lang="css">

    @import "./style/home.css";

    .el-menu-vertical-demo {
        /*width: 230px;*/
    }

    .el-breadcrumb {
        line-height: 60px !important;
    }
</style>
<script>
    import Sidebar from './components/Sidebar'
    import ScrollPane from './components/ScrollPane'
    export default {
        components: { Sidebar, ScrollPane },
        data() {
            return {
                sysName: '后台管理系统',
                sysNameShort: 'ET',
                SidebarVisible: false,//左侧是否可见
                collapsed: false,//左侧菜单是否折叠
                collapsedClass: 'menu-expanded',//折叠样式
                ispc: false,
                sysUserName: '管理员',
                isCollapse: false,
                tagsList: [],//标签页
                routes: []
            }
        },
        methods: {            
            //首页
            toindex() {
                this.$router.replace({
                    path: "/",
                });
            },
            //折叠导航栏
            collapse: function () {
                this.collapsed = !this.collapsed;
                if (this.ispc) {

                    if (this.collapsed) {
                        this.collapsedClass = 'menu-collapsed';
                    } else {
                        this.collapsedClass = 'menu-expanded';
                    }
                } else { // mobile
                    if (this.collapsed) {
                        this.SidebarVisible = true;
                        this.collapsedClass = 'menu-collapsed-mobile';
                    } else {
                        this.SidebarVisible = false;
                        this.collapsedClass = 'menu-expanded-mobile';
                    }

                    this.collapsedClass += ' mobile-ex ';
                }
                this.isCollapse = !this.isCollapse;
            },
            collapseFa: function () {
                if (!this.ispc) {
                    this.collapse();
                }
            },
            //退出登录
            logout: function () {
                var _this = this;
                this.$confirm('确认退出吗?', '提示', {
                }).then(() => {
                    this.tagsList = [];
                    this.routes = [];

                }).catch(() => {

                });
            },
            isActive(path) {
                return path === this.$route.fullPath;
            },
            // 关闭单个标签
            closeTags(index) {
                const delItem = this.tagsList.splice(index, 1)[0];
                const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
                if (item) {
                    delItem.path === this.$route.fullPath && this.$router.push(item.path);

                    this.$store.commit("saveTagsData", JSON.stringify(this.tagsList));
                } else {
                    this.$router.push('/');
                }
            },
            // 设置标签
            setTags(route) {
                if (!route.meta.NoTabPage) {
                    const isExist = this.tagsList.some(item => {
                        return item.path === route.fullPath;
                    })
                    !isExist && this.tagsList.push({
                        title: route.meta.title,
                        path: route.fullPath,
                    })
                }
            },
            // 关闭全部标签
            closeAll() {
                this.tagsList = [];
                this.$router.push('/');
                sessionStorage.removeItem("Tags");
            },
            // 关闭其他标签
            closeOther() {
                const curItem = this.tagsList.filter(item => {
                    return item.path === this.$route.fullPath;
                })
                this.tagsList = curItem;
                sessionStorage.setItem("Tags", JSON.stringify(this.tagsList))
            },
            // 当关闭所有页面时隐藏
            handleTags(command) {
                command === 'other' ? this.closeOther() : this.closeAll();
            }
        },
        mounted() {
            console.log(this.$route)
            var tags = sessionStorage.getItem('Tags') ? JSON.parse(sessionStorage.getItem('Tags')) : [];

            if (tags && tags.length > 0) {
                this.tagsList = tags;
            }
            var NavigationBar = JSON.parse(window.localStorage.router ? window.localStorage.router : null);

            if (this.routes.length <= 0 && NavigationBar && NavigationBar.length >= 0) {
                this.routes = NavigationBar;
            }            
            // 折叠菜单栏
            this.collapse();
        },
        updated() {
            var user = JSON.parse(window.localStorage.user ? window.localStorage.user : null);
            if (user) {
                this.sysUserName = user.uRealName || '未登录';
                this.sysUserAvatar = user.avatar || '../assets/user.png';
            }
            var NavigationBar = JSON.parse(window.localStorage.router ? window.localStorage.router : null);

            if (NavigationBar && NavigationBar.length >= 0) {
                if (this.routes.length <= 0 || (JSON.stringify(this.routes) != JSON.stringify((NavigationBar)))) {
                    this.routes = NavigationBar;
                }
            }

        },
        computed: {
            showTags() {
                if (this.tagsList.length > 1) {
                    this.$store.commit("saveTagsData", JSON.stringify(this.tagsList));
                }
                return this.tagsList.length > 0;
            }
        },
        watch: {
            // 对router进行监听,每当访问router时,对tags的进行修改
            $route: async function (newValue, from) {

                if (global.IS_IDS4) {
                    await this.refreshUserInfo();
                }
                
                this.setTags(newValue);

                const tags = this.$refs.tag
                this.$nextTick(() => {
                    if (tags) {
                        for (const tag of tags) {
                            if (tag.to.path === this.$route.path) {
                                this.$refs.scrollPane.moveToTarget(tag, tags)                     
                                break
                            }
                        }
                    }
                })

            }
        },
        created() {
            this.setTags(this.$route);
            this.ispc = window.screen.width > 680;
            if (this.ispc) {
                this.collapsedClass = 'menu-expanded';
            } else {
                this.collapsedClass = 'menu-expanded-mobile mobile-ex';
                this.collapsed = true;
                this.collapse();
            }
        }

    }
</script>
<style lang="css">
    @import "./style/home.css";

    .el-menu-vertical-demo {
        /*width: 230px;*/
    }

    .el-breadcrumb {
        line-height: 60px !important;
    }
</style>
<style>

    .menu-collapsed .el-icon-arrow-right:before {
        display: none;
    }

    .tags {
        position: relative;
        overflow: hidden;
        border: 1px solid #f0f0f0;
        background: #f0f0f0;
    }

        .tags ul {
            box-sizing: border-box;
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
            display: none;
        }

    .tags-li {
        float: left;
        margin: 3px 5px 2px 3px;
        border-radius: 3px;
        font-size: 13px;
        overflow: hidden;
        height: 23px;
        line-height: 23px;
        border: 1px solid #e9eaec;
        background: #fff;
        padding: 3px 5px 4px 12px;
        vertical-align: middle;
        color: #666;
        -webkit-transition: all .3s ease-in;
        transition: all .3s ease-in;
    }

    .tags-li-icon {
        cursor: pointer;
    }

    .tags-li:not(.active):hover {
        background: #f8f8f8;
    }

    .tags-li-title {
        float: left;
        max-width: 80px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        margin-right: 5px;
        color: #666;
        text-decoration: none;
    }

    .tags-li.active {
        /*color: #fff;*/
        /*border: 1px solid #10B9D3;*/
        /*background-color: #10B9D3;*/
    }

        .tags-li.active .tags-li-title {
            /*color: #fff;*/
        }

    .tags-close-box {
        box-sizing: border-box;
        text-align: center;
        z-index: 10;
        float: right;
        margin-right: 1px;
        line-height: 2;
    }
</style>
<style>

    /*.logoban{*/
    /*width: auto !important;*/
    /*}*/
    .news-dialog {
        background: #fff;
        z-index: 3000 !important;
        position: fixed;
        height: 100vh;
        width: 100%;
        max-width: 260px;
        top: 60px !important;
        left: 0 !important;
        ;
        -webkit-box-shadow: 0 0 15px 0 rgba(0, 0, 0, .05);
        box-shadow: 0 0 15px 0 rgba(0, 0, 0, .05);
        -webkit-transition: all .25s cubic-bezier(.7, .3, .1, 1);
        transition: all .25s cubic-bezier(.7, .3, .1, 1);
        -webkit-transform: translate(100%);
        z-index: 40000;
        left: auto !important;
        ;
        right: 0 !important;
        ;
        transform: translate(0);
    }

        .news-dialog .el-dialog {
            margin: auto !important;
            -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
            box-shadow: none;
            width: 100%;
        }

        .news-dialog.show {
            transform: translate(0);
        }

    .tag-new {
        width: 100%;
        margin: 10px 0;
    }

    @media screen and (max-width: 680px) {

        .collapsedLogo {
            display: none;
        }

        .el-dialog {
            width: 90% !important;
        }

        .content-expanded {
            max-width: 100% !important;
            max-height: calc(100% - 60px);
        }

        .mobile-ex {
            background: #fff;
            z-index: 3000;
            position: fixed;
            height: 100vh;
            width: 100%;
            max-width: 260px;
            top: 0;
            left: 0;
            -webkit-box-shadow: 0 0 15px 0 rgba(0, 0, 0, .05);
            box-shadow: 0 0 15px 0 rgba(0, 0, 0, .05);
            -webkit-transition: all .25s cubic-bezier(.7, .3, .1, 1);
            transition: all .25s cubic-bezier(.7, .3, .1, 1);
            -webkit-transform: translate(100%);
            z-index: 40000;
            left: auto;
            right: 0;
            transform: translate(100%);
        }

            .mobile-ex.menu-collapsed-mobile {
                transform: translate(0);
            }

        .el-menu--collapse {
            width: 100% !important;
        }

        .el-date-editor.el-input, .el-date-editor.el-input__inner, .el-cascader.el-cascader--medium {
            width: 100% !important;
        }

        .toolbar.roles {
            width: 100% !important;
        }

        .toolbar.perms {
            width: 800px !important;
        }

            .toolbar.perms .box-card {
                width: 100% !important;
            }

        .login-container {
            width: 300px !important;
        }

        .count-test label {
        }

        .content-wrapper .tags {
            margin: 0px;
            padding: 0px;
        }

        .activeuser {
            display: none !important;
        }
    }
</style>
<style>


    .tags-view-container {
        height: 34px;
        width: calc(100% - 60px);
        /*background: #fff;*/
        /*border-bottom: 1px solid #d8dce5;*/
        /*box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);*/
        float: left;
    }

        .tags-view-container .tags-view-wrapper .tags-view-item {
            display: inline-block;
            position: relative;
            cursor: pointer;
            height: 26px;
            line-height: 26px;
            border: 1px solid #d8dce5;
            color: #495060;
            background: #fff;
            padding: 0 8px;
            font-size: 12px;
            margin-left: 5px;
            margin-top: 4px;
        }

            .tags-view-container .tags-view-wrapper .tags-view-item:first-of-type {
                margin-left: 15px;
            }

            .tags-view-container .tags-view-wrapper .tags-view-item:last-of-type {
                margin-right: 15px;
            }

            .tags-view-container .tags-view-wrapper .tags-view-item.active {
                /*background-color: #42b983;*/
                /*color: #fff;*/
                /*border-color: #42b983;*/
            }

                .tags-view-container .tags-view-wrapper .tags-view-item.active::before {
                    content: "";
                    background: #2d8cf0;
                    display: inline-block;
                    width: 10px;
                    height: 10px;
                    border-radius: 50%;
                    position: relative;
                    margin-right: 2px;
                }

        .tags-view-container .contextmenu {
            margin: 0;
            background: #fff;
            z-index: 3000;
            position: absolute;
            list-style-type: none;
            padding: 5px 0;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 400;
            color: #333;
            box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
        }

            .tags-view-container .contextmenu li {
                margin: 0;
                padding: 7px 16px;
                cursor: pointer;
            }

                .tags-view-container .contextmenu li:hover {
                    background: #eee;
                }
</style>
<style>

    .tags-view-wrapper .tags-view-item .el-icon-close {
        width: 16px;
        height: 16px;
        vertical-align: 2px;
        border-radius: 50%;
        text-align: center;
        transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
        transform-origin: 100% 50%;
    }

        .tags-view-wrapper .tags-view-item .el-icon-close:before {
            transform: scale(0.6);
            display: inline-block;
            vertical-align: -3px;
        }

        .tags-view-wrapper .tags-view-item .el-icon-close:hover {
            background-color: #ef2b74;
            color: #fff;
        }
</style>
View Code

 

 

4、菜单组件、标签页组件

<template>
  <div>
    <!-- if 有子节点,渲染节点递归 -->
    <template v-if="item.children">
        <!--一级菜单-->
      <el-submenu
        v-if="!(item.path!=''&&item.path!=' '&&item.path!='-')"
        :index="item.id+'index'"
        :key="item.path"
      >
        <template slot="title">
          <i
            v-if="item.children&&item.children.length>0&&item.iconCls"
            class="fa"
            :class="item.iconCls"
          ></i>
          <span class="title-name" slot="title">{{item.name}}</span>
        </template>
        <template v-for="child in item.children">
          <!-- 递归嵌套子菜单 -->
          <template v-if="!child.IsHide">
            <sidebar
              v-if="child.children&&child.children.length>0"
              :item="child"
              :index="child.id"
              :key="child.path"
            />
            <app-link :to="child.path" v-else :key="child.path" :data-link="child.path">
              <el-menu-item :key="child.path" 
          :index="isExternalLink(child.path)? '':child.path"
               @click="cop">
                <i class="fa" :class="child.iconCls"></i>
                <template slot="title">
                  <span class="title-name" slot="title">{{child.name}}</span>
                </template>
              </el-menu-item>
            </app-link>
          </template>
        </template>
      </el-submenu>
      <template v-else>
          <!--一级菜单path不等于空或- -->
        <app-link :to="item.path" :key="item.path+'d'" :data-link="item.path">
          <el-menu-item
          :index="isExternalLink(item.path)? '':item.path"
           :key="item.path+'d'">
            <i class="fa" :class="item.iconCls"></i>
            <template slot="title">
              <span class="title-name" slot="title">{{item.name}}33</span>
            </template>
          </el-menu-item>
        </app-link>
      </template>
    </template>
    <!-- else 没有子节点,直接输出:首页 -->
    <template v-else>
      <app-link :to="item.path" :key="item.path+'d'">
        <el-menu-item
          :index="isExternalLink(item.path)? '':item.path"
          :key="item.path+'d'"
          @click="cop"
        >
          <i class="fa" :class="item.iconCls"></i>
          <template slot="title">
            <span class="title-name" slot="title">{{item.name}}</span>
          </template>
        </el-menu-item>
      </app-link>
    </template>
  </div>
</template>

<script>
import AppLink from "./AppLink";
import { isExternal } from "../js/validate";

export default {
  name: "Sidebar",
  components: { AppLink },
  props: {
    item: {
      type: Object,
      required: true
    }
  },
  methods: {
    isExternalLink(to) {
      return isExternal(to);
    },
    cop: function() {
      // 子组件中触发父组件方法collaFa并传值123
      this.$emit("collaFa", "123");
    }
  }
};
</script>
View Code
<template>
  <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
    <slot />
  </el-scrollbar>
</template>

<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing

export default {
  name: 'ScrollPane',
  data() {
    return {
      left: 0
    }
  },
  computed: {
    scrollWrapper() {
      return this.$refs.scrollContainer.$refs.wrap
    }
  },
  methods: {
    handleScroll(e) {
      const eventDelta = e.wheelDelta || -e.deltaY * 40
      const $scrollWrapper = this.scrollWrapper
      $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
    },
    moveToTarget(currentTag,tagList) {

      const $container = this.$refs.scrollContainer.$el
      const $containerWidth = $container.offsetWidth
      const $scrollWrapper = this.scrollWrapper
      // const tagList = this.$parent.$refs.tag

      let firstTag = null
      let lastTag = null

      // find first tag and last tag
      if (tagList.length > 0) {
        firstTag = tagList[0]
        lastTag = tagList[tagList.length - 1]
      }

      if (firstTag === currentTag) {
        $scrollWrapper.scrollLeft = 0
      } else if (lastTag === currentTag) {
        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
      } else {
        // find preTag and nextTag
        const currentIndex = tagList.findIndex(item => item === currentTag)
        const prevTag = tagList[currentIndex - 1]
        const nextTag = tagList[currentIndex + 1]

        // the tag's offsetLeft after of nextTag
        const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing

        // the tag's offsetLeft before of prevTag
        const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing

        if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
          $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
          $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
        }
      }
    }
  }
}
</script>

<style >
  .scroll-container {
    white-space: nowrap;
    position: relative !important;
    overflow: hidden !important;
    width: 100%;
  }
  .scroll-container  .el-scrollbar__bar {
    bottom: 0px;
  }
  .scroll-container  .el-scrollbar__wrap {
    height: 49px;
  }
</style>
View Code
<template>
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>

<script>
import { isExternal } from '../js/validate'

export default {
  props: {
    to: {
      type: String,
      required: true
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.to)
    },
      type() {         
      if (this.isExternal) {
        return 'a'
      }
      return 'router-link'
    }
  },
  methods: {
    linkProps(to) {
      if (this.isExternal) {
        return {
          href: to,
          target: '_blank',
          style:'color:#fff;'
        }
      }
      return {
        to: to
      }
    }
  }
}
</script>
View Code

 

5、运行效果

 

本文标题为:(十).netcore+vue vue-cli@4+element-ui+router+vuex

基础教程推荐