大家好~ 我是前端uio,本文主要使用vue3和element plus 制作产品的文档说明书, 有什么疑问都欢迎私聊或者在评论区留言。如果需要开发web网页或者微信小程序都可以找我私聊,我会尽可能回答我了解的内容。
一、功能说明
1.目录
(1)多级目录
相比于element-plus官方的示例,本文在使用多级菜单(menu)时,提取重复的逻辑,优化了嵌套结构,从而提高了代码的可读性以及可维护性。
(2)目录所有菜单内容展开
如图所示为初始化页面时目录的效果,可以查看element-plus官方文档,使用default-active属性:页面加载时默认激活菜单的 index,使用default-openeds属性:默认打开的 sub-menu 的 index 的数组。
(3)目录中超出视图的内容可通过滑动滚动条查看
使用div包着el-menu组件,在div的class类中先自定义设置可滚动的高度,再使用 overflow: scroll,即可实现。
2.产品文档说明书内容
(1)点击目录的菜单选项,滑动到指定内容(以置顶为例)
i)获取dom元素
ii)使用el-scrollbar中Exposes出来的其中一个setScrollTop函数(若想滑动到特定位置,可以使用scrollTo,具体见element plus官方文档)
iii)将dom的offsetTop传入setScrollTop函数
二、主要代码
1.目录主要代码(nav.vue)
/**
* @name { el-menu }
* @info { 菜单组件的嵌套优化 }
* @author { 前端uio 2024-5-15 }
*
* @select funtion => 选中菜单项的 index
*/
//目录主要代码
<template>
<div class="nav">
<el-menu
:default-active="nav[0].children[0].index"
:default-openeds="openList"
class="el-menu-vertical-demo"
@select="handleSelect"
>
<div v-for="(item, index) in nav" :key="index">
<el-sub-menu
v-if="item.type === 'sub-menu'"
:key="index" :index="item.index"
>
<template #title>
<span class="titleName">{{ item.title.text }}</span>
</template>
<div v-for="(child, childIndex) in item.children" :key="child.title">
<el-sub-menu
v-if="child.children"
:key="childIndex"
:index="child.index"
>
<template #title>
{{ child.title }}
</template>
<div v-for="(childItem, childItemIndex) in child.children" :key="childItemIndex">
<el-sub-menu :key="childItemIndex" :index="childItem.index">
<template #title>
{{ childItem.text }}
</template>
<div v-for="(children, childrenIndex) in childItem.children" :key="childrenIndex">
<el-menu-item
:index="children.index"
>
{{ children.desc }}
</el-menu-item>
</div>
</el-sub-menu>
</div>
</el-sub-menu>
<el-menu-item
v-else :key="child.index"
:index="child.index"
>
{{ child.title }}
</el-menu-item>
</div>
</el-sub-menu>
<div
v-else-if="item.type === 'menu-item'"
>
<el-menu-item>
<template #title>
<el-icon><component :is="item.title.icon" /></el-icon>
<span>{{ item.title.text }}</span>
</template>
</el-menu-item>
</div>
</div>
</el-menu>
</div>
</template>
<script setup>
import { nav, openList } from '../constants.ts'
const emits = defineEmits(['select'])
const handleSelect = (e) => {
emits("select", e)
}
</script>
2.存放数据(constants.ts)
/**
* @name { typescript }
* @info { 菜单组件的数据 }
* @author { 前端uio 2024-5-15 }
*
* @export array => 数据驱动菜单组件
*/
// 菜单选项
export const nav = [{
type: 'sub-menu',
index: 'text',
title: {
icon: 'location',
text: 'text1'
},
children: [
{
type: 'sub-menu',
title: 'text-1',
index: 'text-1'
},
{
type: 'sub-menu',
index: 'text-2',
title: 'text-2'
},
{
type: 'sub-menu',
index: 'text-3',
title: 'text-3'
},
{
type: 'sub-menu',
index: 'text-4',
title: 'text-4'
},
{
type: 'sub-menu',
index: '1-5',
title: '1-5',
children: [{ type: 'sub-menu', index: '1-5-1', text: 'text',
children: [{
type: 'menu-item', index: '1-5-1-1', desc: 'text'
},
{
type: 'menu-item', index: '1-5-1-2', desc: 'text'
}]
},
{ type: 'sub-menu', index: '1-5-2', text: 'text',
children: [{
type: 'menu-item', index: '1-5-2-1', desc: 'text'
},
{
type: 'menu-item', index: '1-5-2-2', desc: 'text'
},
{
type: 'menu-item', index: '1-5-2-3', desc: 'text'
},
{
type: 'menu-item', index: '1-5-2-4', desc: 'text'
}]
},
{ type: 'sub-menu', index: '1-5-3', text: 'text',
children: [{
type: 'menu-item', index: '1-5-3-1', desc: 'text'
},
{
type: 'menu-item', index: '1-5-3-2', desc: 'text'
},
{
type: 'menu-item', index: '1-5-3-3', desc: 'text'
}]
},
{ type: 'sub-menu', index: '1-5-4', text: 'text',
children: [{
type: 'menu-item', index: '1-5-4-1', desc: 'text'
},
{
type: 'menu-item', index: '1-5-4-2', desc: 'text'
},
{
type: 'menu-item', index: '1-5-4-3', desc: 'text'
}]
}] }
]
}]
// 展开的子菜单
export const openList = [
'text-5-1', 'text-5-2', '1-5', '1-5-1', '1-5-2', '1-5-3', '1-5-4'
]
3.产品内容说明书(content.vue)
/**
* @name { el-scrollbar}
* @info { 产品说明书的主体内容 }
* @author { 前端uio 2024-5-15 }
*
* @ref => 获取组件实例
* @id => 滑动到特定的id元素的位置
*/
<!-- template内容 -->
<div class="fileContent">
<el-scrollbar
ref="scrollView" :max-height="height"
class="content"
>
<div id="fileContent">
<text1 />
</div>
<div id="server">
<server />
</div>
<div id="frameWork">
<frameWork />
</div>
<div id="init">
<init />
</div>
<div id="baseFunc">
<baseFunc />
</div>
</el-scrollbar>
</div>
/**
* @name { proxy}
* @info { 获取element plus 组件exposes 出来的方法 }
* @author { 前端uio 2024-5-15 }
*
* @refs => 获取组件实例
* @setScrollTop => 设置滚动条到顶部的距离
*/
<script setup>
import { ref, getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const selectNav = ref("fileContent")
const isSelect = (e) => {
selectNav.value = e
handleScroll(selectNav.value)
}
const handleScroll = (select) => {
let ele = document.getElementById(select)
proxy.$refs.scrollView.setScrollTop(ele.offsetTop)
}
// 用于存储高度值
const navHeight = ref(0)
navHeight.value = window.innerHeight - 70
const height = `${navHeight.value}px`
</script>
4.注意事项
(1)设置多级目录选项
i)若需要设置子选项,则使用el-sub-menu
ii)若为最后一级的选项,则使用el-menu-item
iii)若目录选项需要设置icon(图标),则使用具名插槽#title
(2)展开所有目录选项
i)动态绑定需要展开的选项index
:default-openeds="openList"
(3) 设置滑动的位置
i)为组件绑定id
ii)getCurrentInstance()函数通过解构赋值获取proxy
const { proxy } = getCurrentInstance()
iii)使用ref绑定el-scrollbar组件,从而获取组件实例
<el-scrollbar ref="scrollView" />
iv)获取element plus 组件exposes 出来的方法setScrollTop将id传入setScrollTop方法
proxy.$refs.scrollView.setScrollTop(ele.offsetTop)
三、拓展与练习
1.水平方向的首页菜单
(1)点击菜单选项切换不同的路由
i)水平菜单
ii)路由切换
(2)菜单选项切换的动画
i)鼠标悬浮或选中菜单选项时,字体加粗且显示下划线
ii)页面初始化时,默认选中第一个菜单选项
2.代码示例
/**
* @name { el-menu}
* @info { 横向菜单 }
* @author { 前端uio 2024-5-15 }
*
*
* @default-active => 设置加载时的激活项
* @mode => 设置为水平模式
* @router => 以index 作为 path 进行路由跳转
* @select funtion => 选中菜单项的 index
*/
<template>
<div class="navBar">
<span class="title">首页菜单</span>
<el-menu
default-active="/index"
mode="horizontal"
router
class="titleMenu"
@select="handleSelect"
>
<div v-for="(item,index) in menuNav" :key="index">
<el-menu-item :index="item.index" :class="item.index===active?'active':''">
{{ item.title }}
</el-menu-item>
</div>
</el-menu>
</div>
</template>
<script setup>
import { ref } from 'vue'
const active = ref('/index')
const handleSelect = (e) => {
active.value = e
}
const menuNav = [
{
title: '首页',
index: '/index'
},
{
title: '菜单项1',
index: '/file'
},
{
title: '菜单项2',
index: '/text2'
},
{
title: '菜单项3',
index: '/text3'
},
{
title: '菜单项4',
index: '/text3'
}
]
</script>
<style scoped>
.navBar {
display: flex;
background-color: #3697c7;
width: 100%;
.title {
letter-spacing: 4px;
font-size: 20px;
font-weight: 800;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
width: 650px;
margin-right: 300px;
}
.titleMenu {
width: 700px;
letter-spacing: 4px;
background-color: #3697c7;
display: flex;
justify-content: space-around;
.el-menu-item {
color: #e5e5e5;
font-size: 18px;
background-color: #3697c7;
}
.el-menu-item:hover , .active{
color: #fff !important;
background-color: #3697c7;
font-weight: 800;
border-bottom: 2px solid #fff;
box-sizing: border-box;
}
}
}
</style>
3.视频展示
可查看本人主页发布的《横向菜单路由切换》视频进行查看
4.具体实现步骤
(1)设置菜单选中效果
:class="item.index===active?'active':''"
(2)动态绑定
通过@select事件返回的index(开启router模式后index为路由路径)动态绑定菜单的选中效果
(3)获取菜单选项所在的样式类名class
无法直接查看element plus封装的组件选项样式,可通过调试工具进行查看,这里的类名(class)为el-menu-item