組件封裝
在上一篇記錄中,首頁(yè)中有太多的代碼,為了避免代碼的臃腫,需要對(duì)主要的功能模塊拆分,來(lái)讓代碼看起來(lái)更簡(jiǎn)潔,且能進(jìn)行復(fù)用。
拆分后還加了些小功能,加入了修改 title 的代碼,修改方式參考vue 動(dòng)態(tài)修改 title。
還增加了當(dāng)前請(qǐng)求的頁(yè)面緩存,使用狀態(tài)管理器處理。監(jiān)聽(tīng)路由,保存到 state 中,來(lái)處理的。 如何監(jiān)聽(tīng)可參考vue 計(jì)算屬性和監(jiān)聽(tīng)屬性。
完整效果圖如下:
首頁(yè)布局拆分后結(jié)構(gòu)
拆分后的,布局結(jié)構(gòu)圖:
拆分后代碼
布局最外層 index 代碼,使用頭部,側(cè)邊欄,主內(nèi)容欄組成,代碼如下:
- <!-- 布局的首頁(yè) -->
- <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>
- <!-- 頭部導(dǎo)航欄 -->
- <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('/')">首頁(yè)</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>
- <!-- 右側(cè)信息 -->
- <div style="float:right">
- <!-- 全屏 -->
- <div style="float:left;line-height: 60px; padding: 0 10px;">
- <i class="el-icon-full-screen" @click="toggleFull"></i>
- </div>
- <!-- 個(gè)人信息 -->
- <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>首頁(yè)</router-link>
- </el-dropdown-item>
- <el-dropdown-item>
- <router-link to="/"><i class="el-icon-s-custom"></i>我的主頁(yè)</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('確認(rèn)退出嗎?', '提示', {
- 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,不能訪問(wèn)ref為空數(shù)組
- //可在這結(jié)束loading,還做一些初始化,實(shí)現(xiàn)函數(shù)自執(zhí)行,
- //可以對(duì)data數(shù)據(jù)進(jìn)行操作,可進(jìn)行一些請(qǐng)求,請(qǐng)求不易過(guò)多,避免白屏?xí)r間太長(zhǎng)。
- created() {},
- //可在這發(fā)起后端請(qǐng)求,拿回?cái)?shù)據(jù),配合路由鉤子做一些事情;可對(duì)DOM 進(jìn)行操作
- mounted() {},
- }
- </script>
- <style lang="scss" scoped>
- .header {
- padding-left: 0px !important;
- height: 60px;
- line-height: 60px;
- width: 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 {
- width: 40px;
- height: 40px;
- border-radius: 10px;
- margin: 10px 0px 10px 10px;
- float: right;
- }
- }
- }
- }
- </style>
頭部中引用的相關(guān)組件代碼如下
折疊導(dǎo)航欄 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: {
- //折疊導(dǎo)航欄
- toggleCollapse: function () {
- this.$store.commit('toggleCollapse')
- },
- },
- }
- </script>
- <style scoped>
- .hamburger {
- padding-left: 13px;
- padding-right: 13px;
- text-align: center;
- width: 34px;
- height: 60px;
- line-height: 60px;
- float: left;
- cursor: pointer;
- }
- .is-active {
- transform: rotate(180deg);
- }
- </style>
折疊導(dǎo)航欄 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 {
- width: 230px;
- }
- .logo-collapse-width {
- width: 65px;
- }
- </style>
側(cè)邊欄下的 index.vue代碼:
- <!-- aside -->
- <template>
- <div class="aside-container" :class="isCollapse ? 'aside-collapse-width' : 'aside-width'">
- <!--導(dǎo)航菜單 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">系統(tǒng)管理</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">導(dǎo)航一</span>
- </el-menu-item>
- <el-menu-item index="3" disabled>
- <i class="el-icon-reading"></i>
- <span slot="title">導(dǎo)航二</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標(biāo)簽頁(yè)選中, 如果不存在則先添加
- 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 {
- width: 230px;
- }
- .aside-collapse-width {
- width: 65px;
- }
- </style>
內(nèi)容模塊下的 index.vue代碼:
- <!-- -->
- <template>
- <div class="main-container clear" :class="isCollapse ? 'position-collapse-left' : 'position-left'">
- <!-- 標(biāo)簽頁(yè) -->
- <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;width:50px;">
- <i class="el-icon-arrow-down"></i>
- </div>
- <el-dropdown-menu slot="dropdown">
- <el-dropdown-item @click.native="tabsCloseCurrentHandle">關(guān)閉當(dāng)前標(biāo)簽</el-dropdown-item>
- <el-dropdown-item @click.native="tabsCloseOtherHandle">關(guān)閉其它標(biāo)簽</el-dropdown-item>
- <el-dropdown-item @click.native="tabsCloseAllHandle">關(guān)閉全部標(biāo)簽</el-dropdown-item>
- <el-dropdown-item @click.native="tabsRefreshCurrentHandle">刷新當(dāng)前標(biāo)簽</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>
- <!-- 主內(nèi)容區(qū)域 -->
- <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) {
- // 當(dāng)只有首頁(yè)時(shí),不允許關(guān)掉。 若是其它頁(yè)面可關(guān)掉后,push 首頁(yè)進(jìn)去
- 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) {
- // 當(dāng)前選中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, 關(guān)閉當(dāng)前
- tabsCloseCurrentHandle() {
- this.removeTabHandle(this.mainTabsActiveName)
- },
- // tabs, 關(guān)閉其它
- tabsCloseOtherHandle() {
- this.mainTabs = this.mainTabs.filter((item) => item.name === this.mainTabsActiveName)
- },
- // tabs, 關(guān)閉全部
- tabsCloseAllHandle() {
- this.mainTabs = []
- this.$router.push('/')
- },
- // tabs, 刷新當(dāng)前
- 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-width: 1px;
- // border-left-style: solid;
- border-bottom-width: 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-width: 1px;
- border-left-style: solid;
- border-bottom-width: 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>
狀態(tài)管理中添加 app 模塊
代碼如下:
- export default {
- state: {
- // 是否折疊導(dǎo)航欄
- isCollapse: false,
- // 訪問(wèn)頁(yè)集合
- mainTabs: [],
- // 當(dāng)前訪問(wèn)頁(yè)名
- 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: {},
- }
當(dāng)然還有一些小的調(diào)整點(diǎn),可參考 git 上的提交版本 首頁(yè)組件拆分
總結(jié)
到此這篇關(guān)于Vue管理系統(tǒng)前端之組件拆分封裝的文章就介紹到這了,更多相關(guān)Vue組件拆分封裝內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/levywang/p/13543734.html