目录
1-自定义校验规则
2-layout组件静态页面搭建
3-logo组件封装
4-左侧菜单静态组件搭建
4.1-动态获取菜单数据
4.2-封装菜单动态展示组件
4.3-配置菜单名称-隐藏-图标属性
4.4-菜单刷新定位当前菜单
5-内容展示区组件封装
1-自定义校验规则
上一篇我们在登录表单进行了基础的表单校验,但是在实际开发项目过程中,我们需要自定义校验规则,比如手机号,身份证号,邮箱等校验...需要使用相关或者复杂的正则表达式来校验。比如我们自定义校验用户名密码(参考element-plus官网form表单自定义校验):
2-layout组件静态页面搭建
我们分析一下,一般的后台管理系统分为三部分,左侧-顶部-内容区三个;我们需要对layout进行三块划分。
组件文件:src\layout\index.vue;将路由登录成功后展示的页面替换为该组件
为了方便开发者根据自己的实际情况,自行决定页面样式的宽-高-颜色等相关参数,我们将一些参数抽取为全局变量,变更一处就好。
<template>
<div class="layout_container">
<!--左侧菜单 -->
<div class="layout_slider">123</div>
<!--顶部导航 -->
<div class="layout_tabbar">456</div>
<!--内容展示区 -->
<div class="layout_main">
789
</div>
</div>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss">
.layout_container{
width: 100%;
height: 100vh;
.layout_slider{
width: $base-menu-width;
height: 100vh;
background: $base-menu-background;
}
.layout_tabbar{
position: fixed;
width: calc(100% - $base-menu-width);
height: $base-tabbar-height;
background: cyan;
top: 0px;
left: $base-menu-width;
}
.layout_main{
position: absolute;
width: calc(100% - $base-menu-width);
height: calc(100vh - $base-tabbar-height);
left: $base-menu-width;
top: $base-tabbar-height;
padding: 20px;
background: yellowgreen;
overflow: auto;//防止内容过多撑起整个页面,导致页面不好看
}
}
</style>
3-logo组件封装
我们将项目菜单上的logo组件进行封装,封装为一个组件src\layout\logo\index.vue在layout组件中引入,我们对logo的标题-图片-展示与否 都可以做成配置文件方便开发者自行决定是否展示。
src\setting.ts内容如下:
//用于项目logo|标题配置
export default {
title: '羊叔运营平台', //项目的标题
logo: '/logo.png', //项目logo设置
logoHidden: true,//logo组件是否隐藏设置
}
4-左侧菜单静态组件搭建
我们需要对左侧菜单进行静态页面开发和搭建,实际项目中,菜单的获取是通过接口获取是动态的,我们需要动态展示菜单。但是有一些路由组件,不是后台返回的,比如常量路由,任意路由和404路由等;所以我们有些路由组件是不需要展示在菜单栏中,我们可以在路由组件中meta中添加字段来控制隐藏和显示,我们通过给meta中添加标题字段来动态展示菜单的标题,我们还需要给菜单名字前面显示图标等,我们将element-plus图标注册为全局组件,然后在路由配置中meta中增加一个图标属性,动态展示菜单。
4.1-动态获取菜单数据
目前因为我们没有调用后台接口,我们先把所有的路由都配置在常量路由里面,所有用户都展示一样的页面菜单。但是我们如何获取所有的路由呢?采用仓库形式获取,在用户小仓库的state对象UserState中定义一个属性menuRoutes,属性的类型是RouteRecordRaw,通过使用这个类型就可以获取到所有的路由配置。
4.2-封装菜单动态展示组件
通过4.1我们可以获取到所有的路由组件信息,这个时候我们将这些信息封装为一个菜单组件,这样只需要传递获取的菜单路由数据,就可以展示。 在src\layout\index.vue文件中,我们使用
菜单组件src\layout\menu\index.vue的内容如下:
<template>
<template v-for="(item) in menuList" :key="item.path">
<!--没有子路由-->
<template v-if="!item.children">
<el-menu-item :index="item.path" v-if="!item.meta.hidden" @click="goRoute">
<el-icon>
<!--component vue框架提供的全局组件,可以直接使用-->
<component :is="item.meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有子路由但是只有一个子路由 -->
<template v-if="item.children && item.children.length == 1">
<el-menu-item :index="item.children[0].path" v-if="!item.children[0].meta.hidden" @click="goRoute">
<el-icon>
<component :is="item.children[0].meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.children[0].meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有子路由且个数大于一个1 -->
<el-sub-menu :index="item.path" v-if="item.children && item.children.length > 1">
<template #title>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<Menu :menuList="item.children"></Menu>
</el-sub-menu>
</template>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
//获取父组件传递过来的全部路由数组
defineProps(['menuList']);
//获取路由器对象
let $router = useRouter();
//点击菜单的回调
const goRoute = (vc: any) => {
//路由跳转
$router.push(vc.index);
}
</script>
<script lang="ts">
export default {
name: 'Menu'
}
</script>
<style scoped></style>
核心逻辑循环遍历路由数据,按照不同的条件展示在页面上。
ps:
1-父子组件通信,采用defineProps来接受数据;
2-需要递归处理菜单,使用添加了一段js代码
<script lang="ts">
export default {
name: 'Menu'
}
</script>
4.3-配置菜单名称-隐藏-图标属性
将图标注册为全局组件,这样我们在项目中就可以直接使用图标的名称进行获取图标。配置方式参考官网:Icon 图标 | Element Plus
我们在路由配置文件中,配置相关的菜单名称,是否隐藏,图标等属性。
4.4-菜单刷新定位当前菜单
当我们点击某个二级菜单时候,点击浏览器刷新,所有菜单都折叠了,不能定位到之前选中的菜单,我们优化一下。需要使用到element-plus菜单组件的default-active属性。
5-内容展示区组件封装
我们将layout的内容展示区,也封装为一个组件,这样我们方便复用,
文件src\layout\main\index.vue的内容如下:
<template>
<!-- 路由组件出口的位置 -->
<router-view v-slot="{ Component }">
<transition name="fade">
<!-- 渲染一级路由的子路由 -->
<component :is="Component" />
</transition>
</router-view>
</template>
<script setup lang="ts">
</script>
<script lang="ts">
export default {
name: 'Main',
}
</script>
<style scoped>
fade-enter-from {
opacity: 0;
transform: scale(0);
}
.fade-enter-active {
transition: all .3s;
}
.fade-enter-to {
opacity: 1;
transform: scale(1);
}
</style>