设计初衷

编写代码过程中最讨厌的就是重复的事情做的多过 3 次,所以在做项目当中自己不多不少都会有一种写复用代码的冲动。

思路

自己看过最多的论坛就是 Laravel 的,里面也有教程这个功能,自己很大一种程度就是想模仿这个功能。

教程有两点:

  1. 菜单栏
  2. 内容跟着菜单联动

image.png

所以这里面我们就明白知道,菜单栏与教程内容是关联关系的。然后看见大部分网上的教程都是采用不同的 url 来对每个菜单进行跳转的。

实践

需求分析明白后,在生成菜单这里就遇到坑。如果是采用不同的 url 的话就会要在生成菜单的时候要填写 url , 而这个 url 明显是要英文字母的。这个有打算使用拼音来解决,不过因为时间成本有点高最后就放弃了。要做这个需求那么我就要将这个组件封装的非常复杂,要动态添加路由进去,这个明显是不符合的。

最后决定使用一开始将所有数据全部查询出来,然后点击不同的菜单,然后赋值到 mavon-editor 这个组件中去。

组件目录结构:

1
2
3
4
5
│  Index.vue

└─Menu
Menu.vue
SubMenu.vue

Index.vue 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<el-row>
<el-col :span="4">
<dMenu :menus="menus" v-on:info="changeInfo"></dMenu>
</el-col>
<el-col :span="20">
<el-row>
<h4 style="text-align: center;">{{ item.title }}</h4>
<mavon-editor
ref="md"
v-model="item.mdCode"
style="padding: 5px 10px;"
:subfield="false"
:defaultOpen="'preview'"
:toolbarsFlag="false"
:editable="false"
:scrollStyle="true"
:ishljs="true"
></mavon-editor>
</el-row>
</el-col>
</el-row>
</template>

这里面就是实现菜单与 markdown 文本展示的代码。

Menu.vue 文件

1
2
3
4
5
<template>
<el-menu :default-active="$route.path" :default-openeds="menusVuex" @open="open" @close="close">
<SubMenu :menus="menus" parent="/" v-bind="$attrs" v-on="$listeners"></SubMenu>
</el-menu>
</template>

这里面可以看见这个 Menu 组件嵌套了一个 SubMenu 子组件,这里面就会涉及一个问题。因为这里要在 Index.vue 点击菜单实现数据变换展示,这里变成了 爷->父->子的三层关系,子要如何调用爷的方法呢?

其实就是使用上面的方法

1
<dMenu :menus="menus" v-on:info="changeInfo"></dMenu>

采用 v-on:info 监听 info 变量的方法,然后父组件将所有的监听和绑定属性传递到子组件中去。

1
<SubMenu :menus="menus" parent="/" v-bind="$attrs" v-on="$listeners"></SubMenu>

采用 v-bin="$attrs" v-on="$listeners" 方法,所以在子组件中我们就可以使用 $emit('info', menu) 进行调用了。

取巧

因为上面的实现方式是采取点击事件切换文本的,所以会存在怎么去保持点击的目录在刷新之后继续展开?

直接在 url 里面拼上菜单的 index

1
2
3
4
5
6
7
8
9
10
open(index) {
let newQuery = JSON.parse(JSON.stringify(this.$route.query));
let _index = [];
if ("tutirialIndex" in newQuery) {
_index = newQuery.tutorialIndex.split(",");
}
_index.push(index);
newQuery.tutorialIndex = _index.join(",");
this.$router.replace({ query: newQuery });
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
close(index) {
let newQuery = JSON.parse(JSON.stringify(this.$route.query));
let _index = [];
if ("tutirialIndex" in newQuery) {
_index = newQuery.tutorialIndex.split(",");
}
let idx = _index.indexOf(index);
if (idx > -1) {
_index.splice(idx, 1);
}
newQuery.tutorialIndex = _index.join(",");
if (newQuery.tutorialIndex == "") {
delete newQuery.tutorialIndex;
}
this.$router.replace({ query: newQuery });
}

然后在 Menu.vue 组件中的 created 函数写入以下代码:

1
2
3
4
5
6
created: function () {
let newQuery = JSON.parse(JSON.stringify(this.$route.query));
if ("tutorialIndex" in newQuery) {
this.openMenus = newQuery.tutorialIndex.split(",");
}
},

这样子我们刷新页面也不怕丢失打开菜单栏了