实现效果

在这里插入图片描述
注:我这里的输入框,搜索的时候是只支持搜索标题,支持模糊搜索

记录实现的主要几步

1、数据处理

在写静态页面的时候需要写假数据,会与后台沟通,这个地方菜单后期会返回什么样的数据结构,有的后台会以前端为准,你想要什么样的数据结构就给你什么样的结构,但这里后台按照他自己的来,直接给前端返回了一个扁平化的数据结构,意思是要自己处理成自己要的结构,没办法,你只得自己处理数据了。
沟通后,后台后面会返回下面这样的数据结构

 data() {
    return {
    // 后台后面会返回的数据结构
      list: [ 
        {
          id: 1,
          parentId: 0,
          fileType: 'dir',
          fileName: '文件夹1'
        },
        {
          id: 2,
          parentId: 1,
          fileType: 'dir',
          fileName: 'api'
        },
        {
          id: 5,
          parentId: 1,
          fileType: 'dir',
          fileName: '文件夹2'
        },
        {
          id: 3,
          parentId: 2,
          fileType: 'file',
          fileName: '获取视频抽帧结果',
          fileContent:
            '### 引用 Blockquotes\n\n> 引用文本 Blockquotes\n\n引用的行内混合 Blockquotes\n\n> 引用:如果想要插入空白换行`即<br />标签`,在插入处先键入两个以上的空格然后回车即可,[普通链接](https://www.mdeditor.com/)。'
        },
        {
          id: 6,
          parentId: 2,
          fileType: 'file',
          fileName: '根据租户和客户获取物联网产品列表',
          fileContent:
            '# 技术文档一级标题名称过长是否影响右边导航展示\n## 服务发布\n### 服务api\n#### 未公开api\n# 根据租户和客户\n###### 获取物联网产品\n\n1. 开发者可以管理的部分主要分为 showcore 轮播图、\n2. 桌面推荐、\n3. 应用模板(适用于带屏设备上的内容展示)和\n4. app 轮播图和内容推荐(适用于控制app上的内容展示)。\n5. 开发者可以根据自身的需求在相应位置进行配置,并通过接入接口(link)来实现前端展示。[百度](https://www.xfyun.cn/?ch=bd05&renqun_youhua=478960&bd_vid=11991992860660649978)![车辆违停](https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=724715456,995074424&fm=15&gp=0.jpg)\n6. ![0NL~UOVXQEBWDKMRXU.png](http://10.40.204.55:80/group1/M00/00/19/CijMN1_sHmuAVP0aAAGeIkymU_g515.png)'
        },
        {
          id: 4,
          parentId: 0,
          fileType: 'dir',
          fileName: '文件夹2'
        },
        {
          id: 7,
          parentId: 4,
          fileType: 'file',
          fileName: '文件',
          fileContent: '我是文件'
        }
      ],
      apilist:[]//这里存放处理后的数据
      selectedKey: [],//展开的api    
    openKeys: [0] /*当前展开的菜单*/,

fileType值为dir代表是文件夹,而fileType值为file代表是文件夹下面的子文件,只有值为file时filecontent才会有内容.
这个地方所以要处理成三级菜单的才能符合需求。
鉴于我原生的不是很好,所以网上搜索了下参考了网上的写法

   // 将后台返回的扁平化数据处理成树形结构
    toTree(data) {
      let result = []
      if (!Array.isArray(data)) {
        return result
      }
      data.forEach((item) => {
        delete item.children
      })
      let map = {}
      data.forEach((item) => {
        map[item.id] = item
      })
      data.forEach((item) => {
        let parent = map[item.parentId]
        if (parent) {
          ;(parent.children || (parent.children = [])).push(item)
        } else {
          result.push(item)
        }
      })

      this.apilist = result
      }

这里是封装的一个方法,等到获取接口数据后直接调用这个方法将获取到的值传递过去,最后处理完后将值赋值给apilist这个数组

2、布局

数据处理好了,这个时候就可以布局了
这里用的antd 的menu菜单这个组件

  <a-menu
            style="width: 214px"
            :default-open-keys="['1']"
            :open-keys.sync="openKeys"
            :mode="mode"
            @openChange="onOpenChange"
            :defaultSelectedKeys="defaultSelectedKeys"
            v-model="selectedKey"
          >
            <template v-for="item in apilist">
              <!-- 第一层 -->
              <template v-if="item.children">
                <a-sub-menu :key="item.id">
                  <span slot="title"
                    ><span>{{ item.fileName }}</span></span
                  >
                  <template v-for="subitem in item.children">
                    <!-- 第二层 -->
                    <template v-if="subitem.children">
                      <a-sub-menu :key="subitem.id" :title="subitem.fileName">
                        <template v-for="sub3 in subitem.children">
                          <!-- 第三层 -->
                          <template v-if="sub3.children">
                            <!-- <a-menu-item :key="sub3.id" @click="setKey(sub3)">
                              {{ sub3.fileName }}
                            </a-menu-item> -->
                            <a-sub-menu :key="sub3.id" :title="sub3.fileName">
                              <template v-for="sub4 in sub3.children">
                                <a-menu-item
                                  :key="sub4.id"
                                  @click="setKey(sub4)"
                                >
                                  {{ sub4.fileName }}
                                </a-menu-item>
                              </template>
                            </a-sub-menu>
                          </template>
                          <!-- 第三层 -->
                          <template v-else>
                            <a-menu-item :key="sub3.id" @click="setKey(sub3)">
                              {{ sub3.fileName }}
                            </a-menu-item>
                          </template>
                        </template>
                      </a-sub-menu>
                    </template>
                    <!-- 第二层 -->
                    <template v-else>
                      <a-menu-item :key="subitem.id" @click="setKey(subitem)">
                        {{ subitem.fileName }}
                      </a-menu-item>
                    </template>
                  </template>
                </a-sub-menu>
              </template>
              <!-- 第一层 -->
              <template v-else>
                <a-menu-item
                  :key="item.id"
                  @click="setKey(item)"
                  :class="item.fileType == 'file' ? 'actstyle' : ''"
                >
                  {{ item.fileName }}
                </a-menu-item>
              </template>
            </template>
          </a-menu>

不会布局的童鞋,可以仔细看下这个注释,每次遍历第一层的时候都会判断有没有children,有就怎样,没有就怎样,如果有就遍历它并判断它还有咩有children属性,有就再接着遍历和判断,每次key绑定时要注意要绑定当前数据的id

3、功能实现,搜索标题则当前菜单高亮,右侧则显示对应的内容

每次点击菜单时,判断它是dir还是file,因为dir是没有content值的,
如果是file就将直接赋值到页面上,并将当前的菜单高亮,这里的selectedkey就是当前高亮的值的id

setKey(item) {
      if (item.fileType == 'file') {
        let activeid = item.id
        this.editorValue = item.fileContent
        this.filename = item.fileName
        this.list.forEach((item, index) => {
          if (item.id == activeid) {
            this.selectedKey = [activeid]
          }
        })
          this.findParent(item.id)  
      } else if (item.fileType == 'dir' && item.children) {
        if (item.children[0].children) {
          this.editorValue = item.children[0].children[0].fileContent
          this.filename = item.children[0].children[0].fileName
          this.selectedKey = [item.children[0].children[0].id]
          this.openKeys=[]
          this.findParent(item.children[0].children[0].id)
        } else {
          this.editorValue = item.children[0].fileContent
          this.filename = item.children[0].fileName
          this.selectedKey = [item.children[0].id]
           this.openKeys=[]
          this.findParent(item.children[0].id)
        }
      }else {
        this.editorValue = item.fileContent
        this.filename = item.fileName
        this.selectedKey = [item.id]
        this.findParent(item.id)
      }
    },

如果是dir,还要判断它有没有children,没有就取children的值,有就取children的children的值

这里封装的findparent方法,是为了在搜索菜单的时候不仅当前菜单要高亮,同时它的前面的父级都要展开,否则搜索到了三级菜单时,没有展开前面的菜单,你就看不见它了

   // 根据元素ID遍历树形结构,查找到所有父元素ID,目的为了展开
    findParent(idx) {
      let pid = []
      this.list.forEach((item) => {
        if (idx === item['id']) {
          pid = item['parentId']
          this.findParent(pid)
          this.openKeys.push(pid)
        }
      })
    },
用到menu菜单时,需要注意它的api

高亮的api selectedKey: []
展开的api openKeys: [0] 当前展开的菜单
这两个api绑定的一般都是它的id

右侧显示content内容

这里我用到的是mavon-editor,可以自行搜索下它的使用方法,这里就不做介绍了, “mavon-editor”: “^2.9.1”,这里我用的是这个版本的。

  <mavon-editor
      v-model="editorValue"
      :toolbarsFlag="false"
      :editable="true"
      :subfield="false"
      :defaultOpen="'preview'"
      class="mavon_editor"
      :toolbars="toolbar_settings"
      :navigation="true"
/>
Logo

前往低代码交流专区

更多推荐