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

  • 相关阅读:
    Tennix — 开源的网球游戏
    Tile Racer — 3D 赛车游戏
    51CTO网管生活
    分割图片的例子 回复 "小熊宝" 的问题
    图解 CSS (5): font 字体
    图解 CSS (9): 列表
    图解 CSS (11): 理解样式表的逻辑
    图解 CSS (8): 浮动、显示、隐藏
    图解 CSS (10): 链接、继承、放缩、鼠标指针、其他(待补充...)
    多线程编程(2) 从 CreateThread 说起
  • 原文地址:https://www.cnblogs.com/levywang/p/13543734.html
Copyright © 2011-2022 走看看