[前端]Vue 00 需求记录

我的第一感觉是前端更实用主义,第一要务是使用现有框架、工具实现界面,所以很多时候前端代码写的并不是那么优雅。但我渐渐发现:很多前端需求其实是类似的,所以只要将大部分需求设计好,并按照需求进行复用,就能够对代码质量有一定保证。

Steven-Zhl 头像
[前端]Vue 00 需求记录

可以使用Element Plus的任意组件库

1. 通用需求

无级菜单

需要构建一个侧边栏菜单,但无法预先知道菜单的层级,需要根据接口的响应动态生成菜单,接口的响应结构如下所示:

[
  {
    "menuName": "item1",
    "hasChild": false,
    "icon": "icon_item1",
    "children": []
  },
  {
    "menuName": "item2",
    "hasChild": true,
    "icon": "icon_item2",
    "children": [
      {
        "menuName": "item2-1",
        "hasChild": false,
        "icon": "icon_item2-1",
        "children": []
      },
      {
        "menuName": "item2-2",
        "hasChild": false,
        "icon": "icon_item2-2",
        "children": []
      }
    ]
  },
  ......
]
  • 通常构建多级菜单都是直接嵌套嘛,在Element Plus中,可以使用<el-menu>作为最外层标签,内部使用<el-sub-menu>作为子菜单,多层子菜单的话就使用<el-sub-menu>进行嵌套,可点击项使用<el-menu-item>。但是明显只能在已知菜单结构的情况下构建,但现在我们并不知道菜单结构。
  • 回看响应结构,我们可以发现:在某一层级中,将会使用hasChild作为一个标志,若hasChildtrue,则当前级别为菜单,若为false,则当前级别为项目。而且,这一规则对接口中每个项目都适用。
  • 所以,我们可以使用递归的方式来构建菜单,递归的终止条件就是hasChildfalse

MyMenu.vue,因为Jekyll代码插件的问题,两层大括号改为了{ { } }以正常显示(这个问题应该和Liquid或者jinja2的语法支持有关)

<template>
  <el-menu style="width: 230px !important" default-active="item1">
    <template v-for="item in routerList">
      <el-menu-item v-if="!item.hasChild" :index="item.menuName" :key="item.menuName">
        { { item.menuName } }
      </el-menu-item>
      <el-sub-menu v-else :index="item.menuName" :key="item.menuName">
        <template #title>{ { item.menuName } }</template>
        <MyMenu :routerList="item.children" />
      </el-sub-menu>
    </template>
  </el-menu>
</template>

<script lang="ts" setup>
  defineProps(['routerList'])
</script>
  • 效果如下所示:

无级菜单1

  • 也许看起来还可以,但还是有些粗糙:不同层级之间应当有不同的缩进,选项应当有不同的图标等等。假设icon字段传递的是Element-Plus图标包中的icon名称,我们稍微美化一下:
<template>
  <el-menu style="width: 230px !important" default-active="item1">
    <template v-for="item in routerList">
      <el-menu-item v-if="!item.hasChild" :index="item.menuName" :key="item.menuName">
        <span :style="{ padding: `${depth * 20}px` }">
          <el-icon><component :is="item.icon" /></el-icon>{ { item.menuName } }
        </span>
      </el-menu-item>

      <el-sub-menu v-else :index="item.menuName" :key="item.menuName">
        <template #title>
          <span :style="{ padding: `${depth * 20}px` }">
            <el-icon><component :is="item.icon" /></el-icon>{ { item.menuName } }
          </span>
        </template>
        <MyMenu :routerList="item.children" :depth="depth + 1" />
      </el-sub-menu>
    </template>
  </el-menu>
</template>

<script lang="ts" setup>
defineProps({
  routerList: {
    type: Array<{ hasChild: Boolean; menuName: String; icon: String; children: Array<any> }>,
    required: true
  },
  depth: {
    type: Number,
    default: 0
  }
})
</script>

无级菜单2

  • 这就有点样子了。