zoukankan      html  css  js  c++  java
  • Vue管理系统前端系列四组件拆分封装


    组件封装

    在上一篇记录中,首页中有太多的代码,为了避免代码的臃肿,需要对主要的功能模块拆分,来让代码看起来更简洁,且能进行复用。

    拆分后还加了些小功能,加入了修改 title 的代码,修改方式参考vue 动态修改 title

    还增加了当前请求的页面缓存,使用状态管理器处理。监听路由,保存到 state 中,来处理的。 如何监听可参考vue 计算属性和监听属性

    完整效果图如下:

    首页布局拆分后结构

    拆分后的,布局结构图:

    拆分后代码

    布局最外层 index 代码,使用头部,侧边栏,主内容栏组成,代码如下:

    <!-- 布局的首页 -->
    <template>
        <div>
            <l-header></l-header>
            <l-aside></l-aside>
            <l-main></l-main>
        </div>
    </template>
    <script>
    import LHeader from './components/header'
    import LAside from './components/aside'
    import LMain from './components/main'
    export default {
        data() {
            return {}
        },
        //引入组件
        components: {
            LHeader,
            LAside,
            LMain,
        },
    }
    </script>
    <style lang="scss" scoped></style>
    

    头部 index.vue 代码:

    <!-- 头部文件 -->
    <template>
        <div class="header">
            <!-- logo -->
            <logo></logo>
            <!-- 折叠按钮 -->
            <hamburger></hamburger>
            <!-- 头部导航栏 -->
            <div class="heardNavBar">
                <el-menu default-active="1" class="el-menu-demo" background-color="#4b5f6e" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
                    <el-menu-item index="1" @click="$router.push('/')">首页</el-menu-item>
                    <el-menu-item index="2" @click="openUrl('#')">使用文档</el-menu-item>
                    <el-menu-item index="3" @click="openUrl('https://github.com/levy-w-wang/lion-ui')">GitHub</el-menu-item>
                </el-menu>
            </div>
            <!-- 右侧信息 -->
            <div style="float:right">
                <!-- 全屏 -->
                <div style="float:left;line-height: 60px; padding: 0 10px;">
                    <i class="el-icon-full-screen" @click="toggleFull"></i>
                </div>
                <!-- 个人信息 -->
                <div class="userinfo">
                    <el-dropdown trigger="hover">
                        <span class="el-dropdown-link userinfo-inner">
                            <img src="@assets/img/user.jpg" />
                            {{ $store.getters.userInfo.username }}<i class="el-icon-caret-bottom"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>
                                <router-link to="/"><i class="el-icon-s-home"></i>首页</router-link>
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <router-link to="/"><i class="el-icon-s-custom"></i>我的主页</router-link>
                            </el-dropdown-item>
                            <el-dropdown-item divided>
                                <a @click="loginOut()"><i class="el-icon-switch-button"></i>登出</a>
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </div>
            </div>
        </div>
    </template>
    
    <script>
    import screenfull from 'screenfull'
    import hamburger from './hamburger'
    import logo from './logo'
    // import { mapState } from 'vuex'
    export default {
        data() {
            return {}
        },
        computed: {
            // ...mapState({
            //     isCollapse: (state) => state.app.isCollapse,
            // }),
        },
        //引入组件
        components: {
            hamburger,
            logo,
        },
        // 方法
        methods: {
            openUrl(url) {
                window.open(url)
            },
            loginOut() {
                this.$confirm('确认退出吗?', '提示', {
                    type: 'warning',
                })
                    .then(() => {
                        this.$store.commit('logout')
                    })
                    .catch(() => {})
            },
            toggleFull() {
                if (!screenfull.isEnabled) {
                    this.$message({
                        type: 'warning',
                        message: 'you browser can not work',
                    })
                    return false
                }
                screenfull.toggle()
            },
        },
        //未挂载DOM,不能访问ref为空数组
        //可在这结束loading,还做一些初始化,实现函数自执行,
        //可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。
        created() {},
        //可在这发起后端请求,拿回数据,配合路由钩子做一些事情;可对DOM 进行操作
        mounted() {},
    }
    </script>
    
    <style lang="scss" scoped>
    .header {
        padding-left: 0px !important;
        height: 60px;
        line-height: 60px;
         100%;
        background: #4b5f6e;
        color: #fff;
    
        .heardNavBar {
            float: left;
            background: #4b5f6e;
            padding: 0px 0px;
            height: 60px;
            line-height: 60px;
            font-size: 28px;
            cursor: pointer;
        }
    
        .userinfo {
            text-align: right;
            padding-right: 24px;
            float: right;
            padding: 0 10px;
            .userinfo-inner {
                font-size: 20px;
                cursor: pointer;
                color: #fff;
                img {
                     40px;
                    height: 40px;
                    border-radius: 10px;
                    margin: 10px 0px 10px 10px;
                    float: right;
                }
            }
        }
    }
    </style>
    

    头部中引用的相关组件代码如下

    折叠导航栏 hamburger 下的 index.vue 代码:

    <template>
        <div @click="toggleCollapse">
            <svg :class="{ 'is-active': !isCollapse }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
                <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
            </svg>
        </div>
    </template>
    
    <script>
    import { mapState } from 'vuex'
    export default {
        name: 'Hamburger',
        computed: {
            ...mapState({
                isCollapse: (state) => state.app.isCollapse,
            }),
        },
        methods: {
            //折叠导航栏
            toggleCollapse: function () {
                this.$store.commit('toggleCollapse')
            },
        },
    }
    </script>
    
    <style scoped>
    .hamburger {
        padding-left: 13px;
        padding-right: 13px;
        text-align: center;
         34px;
        height: 60px;
        line-height: 60px;
        float: left;
        cursor: pointer;
    }
    
    .is-active {
        transform: rotate(180deg);
    }
    </style>
    

    折叠导航栏 logo 下的 index.vue 代码:

    <!--  -->
    <template>
        <div class="logo" :class="isCollapse ? 'logo-collapse-width' : 'logo-width'">
            <img v-if="isCollapse" src="@assets/logo6065.png" @click="$router.push('/')" />
            <img v-else src="@assets/logo.png" @click="$router.push('/')" />
        </div>
    </template>
    
    <script>
    import { mapState } from 'vuex'
    export default {
        data() {
            return {}
        },
        computed: {
            ...mapState({
                isCollapse: (state) => state.app.isCollapse,
            }),
        },
    }
    </script>
    <style lang="scss" scoped>
    .logo {
        float: left;
        height: 60px;
        padding: 0;
        margin: 0;
    }
    .logo-width {
         230px;
    }
    .logo-collapse-width {
         65px;
    }
    </style>
    

    侧边栏下的 index.vue代码:

    <!-- aside -->
    <template>
        <div class="aside-container" :class="isCollapse ? 'aside-collapse-width' : 'aside-width'">
            <!--导航菜单  default-active="1-1"-->
            <el-menu class="el-menu-vertical-demo" :class="isCollapse ? 'aside-collapse-width' : 'aside-width'" :collapse-transition="false" :unique-opened="true" :collapse="isCollapse">
                <el-submenu index="1">
                    <template slot="title">
                        <i class="el-icon-setting"></i>
                        <span slot="title">系统管理</span>
                    </template>
                    <el-menu-item index="1-1" @click="$router.push('usermanage')">用户管理</el-menu-item>
                    <el-menu-item index="1-2" @click="$router.push('menumanage')">菜单管理</el-menu-item>
                </el-submenu>
                <el-menu-item index="2" disabled>
                    <i class="el-icon-magic-stick"></i>
                    <span slot="title">导航一</span>
                </el-menu-item>
                <el-menu-item index="3" disabled>
                    <i class="el-icon-reading"></i>
                    <span slot="title">导航二</span>
                </el-menu-item>
            </el-menu>
        </div>
    </template>
    
    <script>
    import { mapState } from 'vuex'
    export default {
        data() {
            return {}
        },
        //$store.getters.isCollapse
        computed: {
            ...mapState({
                isCollapse: (state) => state.app.isCollapse,
            }),
            mainTabs: {
                get() {
                    return this.$store.state.app.mainTabs
                },
                set(val) {
                    this.$store.commit('updateMainTabs', val)
                },
            },
            mainTabsActiveName: {
                get() {
                    return this.$store.state.app.mainTabsActiveName
                },
                set(val) {
                    this.$store.commit('updateMainTabsActiveName', val)
                },
            },
        },
        watch: {
            $route: 'handleRoute',
        },
        created() {
            console.log(this.$route)
            this.handleRoute(this.$route)
        },
        methods: {
            // 路由操作处理
            handleRoute(route) {
                // tab标签页选中, 如果不存在则先添加
                var tab = this.mainTabs.filter((item) => item.name === route.name)[0]
                if (!tab) {
                    tab = {
                        name: route.name,
                        title: route.meta.title,
                        icon: route.meta.icon,
                    }
                    this.mainTabs = this.mainTabs.concat(tab)
                }
                this.mainTabsActiveName = tab.name
            },
        },
    }
    </script>
    <style lang="scss" scoped>
    .aside-container {
        position: fixed;
        top: 0px;
        left: 0;
        bottom: 0;
        z-index: 1020;
        .el-menu {
            position: absolute;
            top: 60px;
            bottom: 0px;
            text-align: left;
        }
    }
    .aside-width {
         230px;
    }
    .aside-collapse-width {
         65px;
    }
    </style>
    

    内容模块下的 index.vue代码:

    <!--  -->
    <template>
        <div class="main-container clear" :class="isCollapse ? 'position-collapse-left' : 'position-left'">
            <!-- 标签页 -->
            <el-tabs class="tabs" :class="isCollapse ? 'position-collapse-left' : 'position-left'" v-model="mainTabsActiveName" :closable="true" type="card" @tab-click="selectedTabHandle" @tab-remove="removeTabHandle">
                <el-dropdown class="tabs-tools" :show-timeout="0" trigger="hover">
                    <div style="font-size:20px;50px;">
                        <i class="el-icon-arrow-down"></i>
                    </div>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item @click.native="tabsCloseCurrentHandle">关闭当前标签</el-dropdown-item>
                        <el-dropdown-item @click.native="tabsCloseOtherHandle">关闭其它标签</el-dropdown-item>
                        <el-dropdown-item @click.native="tabsCloseAllHandle">关闭全部标签</el-dropdown-item>
                        <el-dropdown-item @click.native="tabsRefreshCurrentHandle">刷新当前标签</el-dropdown-item>
                    </el-dropdown-menu>
                </el-dropdown>
                <el-tab-pane v-for="item in mainTabs" :key="item.name" :label="item.title" :name="item.name">
                    <span slot="label"> <i :class="item.icon"></i> {{ item.title }} </span>
                </el-tab-pane>
            </el-tabs>
    
            <!-- 主内容区域 -->
            <div class="main-content">
                <keep-alive>
                    <transition name="fade" mode="out-in">
                        <router-view></router-view>
                    </transition>
                </keep-alive>
            </div>
        </div>
    </template>
    
    <script>
    import { mapState } from 'vuex'
    export default {
        data() {
            return {}
        },
        computed: {
            ...mapState({
                isCollapse: (state) => state.app.isCollapse,
            }),
            mainTabs: {
                get() {
                    return this.$store.state.app.mainTabs
                },
                set(val) {
                    this.$store.commit('updateMainTabs', val)
                },
            },
            mainTabsActiveName: {
                get() {
                    return this.$store.state.app.mainTabsActiveName
                },
                set(val) {
                    this.$store.commit('updateMainTabsActiveName', val)
                },
            },
        },
        methods: {
            // tabs, 选中tab
            selectedTabHandle(tab) {
                tab = this.mainTabs.filter((item) => item.name === tab.name)
                if (tab.length >= 1) {
                    this.$router.push({ name: tab[0].name })
                }
            },
            // tabs, 删除tab
            removeTabHandle(tabName) {
                // 当只有首页时,不允许关掉。 若是其它页面可关掉后,push 首页进去
                if (this.mainTabs.length == 1 && this.mainTabs[0].name == 'index') {
                    return
                }
                this.mainTabs = this.mainTabs.filter((item) => item.name !== tabName)
                if (this.mainTabs.length >= 1) {
                    // 当前选中tab被删除
                    if (tabName === this.mainTabsActiveName) {
                        this.$router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => {
                            this.mainTabsActiveName = this.$route.name
                        })
                    }
                } else {
                    this.$router.push('/')
                }
            },
            // tabs, 关闭当前
            tabsCloseCurrentHandle() {
                this.removeTabHandle(this.mainTabsActiveName)
            },
            // tabs, 关闭其它
            tabsCloseOtherHandle() {
                this.mainTabs = this.mainTabs.filter((item) => item.name === this.mainTabsActiveName)
            },
            // tabs, 关闭全部
            tabsCloseAllHandle() {
                this.mainTabs = []
                this.$router.push('/')
            },
            // tabs, 刷新当前
            tabsRefreshCurrentHandle() {
                var tempTabName = this.mainTabsActiveName
                this.removeTabHandle(tempTabName)
                this.$nextTick(() => {
                    this.$router.push({ name: tempTabName })
                })
            },
        },
    }
    </script>
    <style lang="scss" scoped>
    .main-container {
        padding: 0 5px 5px;
        position: absolute;
        top: 60px;
        left: 1px;
        right: 1px;
        bottom: 0px;
        .tabs {
            position: fixed;
            top: 60px;
            right: 50px;
            padding-left: 0px;
            padding-right: 2px;
            z-index: 1020;
            height: 40px;
            line-height: 40px;
            font-size: 14px;
            background: rgb(255, 253, 255);
            border-color: rgba(200, 206, 206, 0.5);
            // border-left- 1px;
            // border-left-style: solid;
            border-bottom- 1px;
            border-bottom-style: solid;
        }
        .tabs-tools {
            position: fixed;
            top: 60px;
            right: 0;
            z-index: 1020;
            height: 40px;
            // padding: 0 10px;
            font-size: 14px;
            line-height: 40px;
            cursor: pointer;
            border-color: rgba(200, 206, 206, 0.5);
            border-left- 1px;
            border-left-style: solid;
            border-bottom- 1px;
            border-bottom-style: solid;
            background: rgba(255, 255, 255, 1);
        }
        .tabs-tools:hover {
            background: rgba(200, 206, 206, 1);
        }
        .main-content {
            position: absolute;
            top: 45px;
            left: 5px;
            right: 5px;
            bottom: 5px;
            padding: 5px;
            // background: rgba(209, 212, 212, 0.5);
        }
    }
    .position-left {
        left: 230px;
    }
    .position-collapse-left {
        left: 65px;
    }
    </style>
    

    状态管理中添加 app 模块

    代码如下:

    export default {
        state: {
            // 是否折叠导航栏
            isCollapse: false,
            // 访问页集合
            mainTabs: [],
            // 当前访问页名
            mainTabsActiveName: '',
        },
        getters: {
            isCollapse: (state) => {
                return state.isCollapse
            },
        },
        mutations: {
            toggleCollapse(state) {
                state.isCollapse = !state.isCollapse
            },
            updateMainTabs(state, tabs) {
                state.mainTabs = tabs
            },
            updateMainTabsActiveName(state, name) {
                state.mainTabsActiveName = name
            },
        },
        actions: {},
    }
    

    当然还有一些小的调整点,可参考 git 上的提交版本 首页组件拆分

    原文地址:http://book.levy.net.cn/doc/frontend/uiframe/module_split.html

  • 相关阅读:
    【Uvalive4960】 Sensor network (苗条树,进化版)
    【UVA 1151】 Buy or Build (有某些特别的东东的最小生成树)
    【UVA 1395】 Slim Span (苗条树)
    【UVA 10600】 ACM Contest and Blackout(最小生成树和次小生成树)
    【UVA 10369】 Arctic Network (最小生成树)
    【UVA 10816】 Travel in Desert (最小瓶颈树+最短路)
    【UVA 11183】 Teen Girl Squad (定根MDST)
    【UVA 11865】 Stream My Contest (二分+MDST最小树形图)
    【UVA 11354】 Bond (最小瓶颈生成树、树上倍增)
    【LA 5713 】 Qin Shi Huang's National Road System (MST)
  • 原文地址:https://www.cnblogs.com/levywang/p/13543734.html
Copyright © 2011-2022 走看看