前言
继续上篇的文章,本文继续进行搭建自己风格的后台管理系统,自定义页面布局和侧边栏,面包屑等页面基础功能。
项目文件树
上次文章只是提到了文件名,这次就把整个的文件树给结构放出来,及自己对文件夹的理解,顺便为以后的文章做做预告。
api
这里是存放所有的请求接口,先分公共数据接口和普通数据接口,然后普通数据接口再进行模块划分。
assets
这里是配置一些公共的样式和图片,assets图片和static文件下的图片在HTML都是正常使用的,但是在js中使用的话,路径要经过webpack中的file-loader编译,
路径不能直接写,需要用require引用。
举个栗子
<img :src="require('@/assets/img/loginImg.png')" />
data() {
return {
sysList: [
{
id: 1,
logo: require("@/assets/img/stage/service.png"),
title: "系统",
hidden: true
},
{
id: 2,
logo: require("@/assets/img/stage/user.png"),
title: "中心",
hidden: true
}
]
}
}
components
这里是放置公共组件的地方,写后台管理系统,一般elementUI的组件就基本上足够使用了,但是避免不了有些需求会写一些新组件,或者更多复用组件,减少CV
大法。我对于这里的理解是,全局用的组件就直接以组件名为文件夹的命名写一个组件,模块组件就以模块名为文件夹的命名,在里面写组件。
layout
这里就是项目的整体布局的文件夹,如果有关页面的整体布局就是在这里修改。等下就回着重说一下这里的内容。
router
这里是vue的路由配置的地方。关于路由配置按照自己的项目实际情况进行调试。
store
这里是放全局数据的地方,使用的是vuex,状态管理模式。我的理解和组件那边类似,按照模块去给每一个modules命名,公共数据写在单独的modules中,getters
引入所有的数据,同时对数据进行处理。
utils
这里是放置公共方法,request封装请求等的地方。
views
这里是写项目中每一个页面的地方,我对此的理解是,按模块划分不同的文件夹,不同模块下定义对应页面名称的文件夹,然后在里面添加2个文件, 对应的名称分别
是index.vue,index.less,每个模块下的页面,重复性比较大的页面,可以共用一个页面,路由切换可以通过wacth去监听$route(to, from), 值得注意的地方
是,刷新页面不会触发wacth监听的$route。
App.vue
这是根组件,就不过多介绍了,懂得都懂。
main.js
这里是项目的入口文件,所有页面都会加载这个文件,它的作用是实例化Vue,引入一些常用的公共功能和全局组件等。常用的公共功能有axios,vuex,router,权
限控制等。
permission.js
这里是路由的钩子beforeEach,通过对权限的判断,来确定能不能跳转。我对此处的理解是,vuex状态管理模式,但是当页面刷新的时候,所有数据就会消失,所以
需要在此处配置,以便刷新是继续获取到数据。
theme
这里是的在elementui官网,主题里面自定义的主题,后引用到项目中。
.env系列
这里是开发环境、测试环境和生产环境的配置。
.gitlab-ci.yml
这里是gitlab的CI工作流的配置,配置了一些语法指令。当代码上传到gitlab后,通过CI/CD调起dockerfile文件,去执行不同操作。
dockerfile系列
这里是dockerfile配置,通过gitlab的CI/CD工作流执行不同的dockerfile操作,最终实现自动化打包上测试环境/生产环境。
babel.config.js
这里是babel的配置
vue.config.js
这里是一个可选的vue配置文件,比如打包后的输出文件目录、放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录、devServer代理
和disableHostCheck等。
布局配置
开始今天的正文,自定义项目的页面布局。
页面文件布局和vue-element-admin保持高度一致,但是内容相对减少了许多,还是那句话,根据vue-element-admin修改成适用自己的框架。 这个是页面布局大致样子,可以根据自己的喜好去调整。简单介绍一下,左边aside是侧边栏,header中,左边是面包屑,右边是用户头像和一下拉选项卡等操作。main是项目页面的主要显示地方。
layout index.vue
<template>
<div>
<el-container>
<el-aside width="210px" class="side">
<side-bar></side-bar>
</el-aside>
<el-container>
<el-header height="63px">
<nav-bar></nav-bar>
</el-header>
<el-main>
<keep-alive>
<router-view />
</keep-alive>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import NavBar from "./components/NavBar"
import SideBar from "./components/Sidebar/index"
export default {
name: "layout",
components: {
"nav-bar": NavBar,
"side-bar": SideBar
}
}
</script>
<style lang="less" scoped>
// 清除el-header的padding:0 20px;的默认样式
/deep/ .el-header{
padding: 0;
}
/deep/ .el-main {
background: #F0F4FA;
overflow: auto;
height: calc(100vh - 63px);
}
.side{
min-height: 100vh;
box-shadow: 2px 0px 8px 0 #d2dbe8;
z-index: 1;
}
</style>
layout-components-navbar.vue
<template>
<div mode="horizontal" class="headerBar">
<div class="headerBar__loginSwitch">
<img src="@/assets/img/switch.png" @click="changeCollapse" />
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item to="/xxx">{{ navbar }}</el-breadcrumb-item>
<el-breadcrumb-item v-for="(item, index) in matchedRoute" :key="index" :to="item.path">
{{item.name}}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="headerBar__loginInfo">
<img :src="require('@/assets/img/loginImg.png')" />
<el-dropdown class="user" trigger="click" @command="handleCommand">
<span>
zhongshi<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<!-- <el-dropdown-item command="user">个人中心</el-dropdown-item> -->
<el-dropdown-item command="back">返回首页</el-dropdown-item>
<el-dropdown-item command="quit">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
layout-components-sidebar-index.vue
<template>
<div class="navWrap">
<div class="logo">
<img src="@/assets/img/logo.png" @click="toStage">
</div>
<el-scrollbar>
<el-menu
:default-active="activeMenu"
:unique-opened="true"
:collapse-transition="false"
:collapse="false"
mode="vertical"
router>
<sidebar-item v-for="route in routes" :key="route.path" :item="route" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import SidebarItem from './SidebarItem'
import { mapGetters } from 'vuex'
export default {
components:{
SidebarItem
},
computed: {
...mapGetters([
"routes"
]),
activeMenu() {
const route = this.$route
const { meta, path } = route
return path
},
collapse() {
return this.$store.state.settings.collapse
}
},
methods: {
toStage() {
this.$router.replace('/stage')
}
}
}
</script>
<style lang="less" scoped >
.navWrap {
height: 100%;
background: #fff;
box-shadow: 2px 0 8px 0 #D2DBE8;
.logo {
width: 200px;
height: 63px;
text-align: center;
line-height: 63px;
img{
width: 171px;
height: 27px;
vertical-align: middle;
cursor: pointer;
}
}
.el-menu {
height: 100%;
border: none;
}
.el-menu-item.is-active {
background-color: #f0f4fa;
color: #3e4552;
font-size: 14px;
border-left: 3px solid #3d71ff;
}
}
</style>
layout-components-sidebar-sidebaritem.vue
这里是展示侧边栏最主要的地方,我目前只做到了能够使用,但是对于完美使用还有一些距离,侧边栏小图片暂时还没加入进去,然后无限循环也没有加入进去,也就是vue-element-admin递归配置每一项我没有摸透,就没有采用。处于待优化状态
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChildren(item.children)">
<el-menu-item :index="item.path" :key="item.path">
<span slot="title">
{{ item.name }}</span>
</el-menu-item>
</template>
<template v-else>
<el-submenu :index="item.path + '/' + item.children[0].path" :key = "item.children[0].name">
<template slot="title">
<span slot="title">{{ item.name }}</span>
</template>
<template v-for="child in filterChildren(item.children)">
<el-submenu v-if="child.children && child.children.length > 0 && hasOneShowingChildren(child.children)"
:index="item.path+'/'+child.path"
:key="child.name">
<template slot="title">
<span>{{child.name}}</span>
</template>
</el-submenu>
<el-menu-item v-else
:index="item.path+'/'+child.path"
:key="child.name">
<span>{{ child.name }}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
import path from "path"
export default {
name: "SidebarItem",
props: {
item: {
type: Object,
required: true
},
},
computed: {
filterChildren() {
return function(children) {
return children.filter(i => !i.hidden)
}
}
},
data() {
this.onlyOneChild = null;
return {}
},
methods: {
hasOneShowingChildren(children = []) {
const showingChildren = children.filter(item => {
return !item.hidden;
});
if (showingChildren.length === 1) return false
return false;
}
}
}
</script>
<style lang="less" scoped>
.el-menu-item.is-active {
background-color: #f0f4fa;
color: #3d71ff;
font-size: 14px;
border-left: 3px solid #3d71ff;
}
</style>
其他
侧边栏都出来了,肯定是要配置路由了呀,我这边采用的是动态路由配置。
router-index.js
import Vue from "vue";
import Router from "vue-router";
// 第三方库需要use一下才能用
Vue.use(Router)
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
export const defaultRoutes = [
{
path: "/",
component: () => import("@/views/login"),
hidden: true,
},
{
path: "/login",
name: "登录",
component: () => import("@/views/login"),
meta: { title: "登录", icon: "dashboard", affix: true },
hidden: true,
},
]
const router = new Router({
routes: defaultRoutes
});
export default router
router-user.js
export default [{
path: "/user",
component: "layout/index",
redirect: "/user/set",
hidden: false,
name: "个人管理",
children: [
{
path: "set",
name: "个人设置",
component: "views/user/set/index",
meta: { title: "个人设置", icon: "dashboard", affix: true },
}
]
}]
总结
本文到这里就结束了,下一篇文章就是关于动态路由的配置和使用。相信就在不太久将来。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!