当前位置: 首页 > article >正文

使用AI一步一步实现若依前端(5)

功能5:侧边栏菜单动态显示

功能4:首页使用Layout布局
功能3:点击登录按钮实现页面跳转
功能2:静态登录界面
功能1:创建前端项目

前言

在若依中,侧边栏显示的菜单项,是根据登录用户的角色动态显示的。不同角色的用户,看到的菜单可能是不一样的。
这就需要在代码中,能根据后端服务器返回的结果,动态显示菜单项。

一.操作步骤

1.获取getRouters接口的返回结果

{"msg": "操作成功","code": 200,"data": [{"name": "System","path": "/system","hidden": false,"redirect": "noRedirect","component": "Layout","alwaysShow": true,"meta": {"title": "系统管理","icon": "system","noCache": false,"link": null},"children": [{"name": "User","path": "user","hidden": false,"component": "system/user/index","meta": {"title": "用户管理","icon": "user","noCache": false,"link": null}},{"name": "Role","path": "role","hidden": false,"component": "system/role/index","meta": {"title": "角色管理","icon": "peoples","noCache": false,"link": null}},{"name": "Menu","path": "menu","hidden": false,"component": "system/menu/index","meta": {"title": "菜单管理","icon": "tree-table","noCache": false,"link": null}},{"name": "Dept","path": "dept","hidden": false,"component": "system/dept/index","meta": {"title": "部门管理","icon": "tree","noCache": false,"link": null}},{"name": "Post","path": "post","hidden": false,"component": "system/post/index","meta": {"title": "岗位管理","icon": "post","noCache": false,"link": null}},{"name": "Dict","path": "dict","hidden": false,"component": "system/dict/index","meta": {"title": "字典管理","icon": "dict","noCache": false,"link": null}},{"name": "Config","path": "config","hidden": false,"component": "system/config/index","meta": {"title": "参数设置","icon": "edit","noCache": false,"link": null}},{"name": "Notice","path": "notice","hidden": false,"component": "system/notice/index","meta": {"title": "通知公告","icon": "message","noCache": false,"link": null}},{"name": "Log","path": "log","hidden": false,"redirect": "noRedirect","component": "ParentView","alwaysShow": true,"meta": {"title": "日志管理","icon": "log","noCache": false,"link": null},"children": [{"name": "Operlog","path": "operlog","hidden": false,"component": "monitor/operlog/index","meta": {"title": "操作日志","icon": "form","noCache": false,"link": null}},{"name": "Logininfor","path": "logininfor","hidden": false,"component": "monitor/logininfor/index","meta": {"title": "登录日志","icon": "logininfor","noCache": false,"link": null}}]}]},{"name": "Monitor","path": "/monitor","hidden": false,"redirect": "noRedirect","component": "Layout","alwaysShow": true,"meta": {"title": "系统监控","icon": "monitor","noCache": false,"link": null},"children": [{"name": "Online","path": "online","hidden": false,"component": "monitor/online/index","meta": {"title": "在线用户","icon": "online","noCache": false,"link": null}},{"name": "Job","path": "job","hidden": false,"component": "monitor/job/index","meta": {"title": "定时任务","icon": "job","noCache": false,"link": null}},{"name": "Druid","path": "druid","hidden": false,"component": "monitor/druid/index","meta": {"title": "数据监控","icon": "druid","noCache": false,"link": null}},{"name": "Server","path": "server","hidden": false,"component": "monitor/server/index","meta": {"title": "服务监控","icon": "server","noCache": false,"link": null}},{"name": "Cache","path": "cache","hidden": false,"component": "monitor/cache/index","meta": {"title": "缓存监控","icon": "redis","noCache": false,"link": null}},{"name": "CacheList","path": "cacheList","hidden": false,"component": "monitor/cache/list","meta": {"title": "缓存列表","icon": "redis-list","noCache": false,"link": null}}]},{"name": "Tool","path": "/tool","hidden": false,"redirect": "noRedirect","component": "Layout","alwaysShow": true,"meta": {"title": "系统工具","icon": "tool","noCache": false,"link": null},"children": [{"name": "Build","path": "build","hidden": false,"component": "tool/build/index","meta": {"title": "表单构建","icon": "build","noCache": false,"link": null}},{"name": "Gen","path": "gen","hidden": false,"component": "tool/gen/index","meta": {"title": "代码生成","icon": "code","noCache": false,"link": null}},{"name": "Swagger","path": "swagger","hidden": false,"component": "tool/swagger/index","meta": {"title": "系统接口","icon": "swagger","noCache": false,"link": null}}]},{"name": "Http://ruoyi.vip","path": "http://ruoyi.vip","hidden": false,"component": "Layout","meta": {"title": "若依官网","icon": "guide","noCache": false,"link": "http://ruoyi.vip"}}]}

2.向AI提问,生成代码

尽量的把返回结果的数据结构描述清楚,让AI给出对应的代码。
在这里插入图片描述
分析AI给出的代码,做适当的修改。
在这里插入图片描述

2.SidebarMenu.vue

复制AI的代码。

<!-- SidebarMenu.vue -->
<template>
  <el-menu
    router
    default-active="/"
    class="el-menu-vertical"
    :collapse="false"
  >
    <template v-for="item in menuData" :key="item.path">
      <MenuItem :item="item" :level="0" />
    </template>
  </el-menu>
</template>

<script setup>
import { defineProps } from 'vue';
import MenuItem from './MenuItem.vue';

defineProps({
  menuData: {
    type: Array,
    required: true
  }
});
</script>

3.MenuItem.vue

新建文件src\layout\components\MenuItem.vue。将AI生成的代码复制到文件内。

<!-- MenuItem.vue -->
<template>
  <template v-if="hasChildren">
    <el-sub-menu :index="item.path">
      <template #title>
        <el-icon v-if="item.meta?.icon">
          <component :is="item.meta.icon" />
        </el-icon>
        <span>{{ item.meta?.title }}</span>
      </template>
      <template v-for="child in item.children" :key="child.path">
        <MenuItem :item="child" :level="level + 1" />
      </template>
    </el-sub-menu>
  </template>
  
  <template v-else>
    <el-menu-item :index="item.path">
      <el-icon v-if="item.meta?.icon">
        <component :is="item.meta.icon" />
      </el-icon>
      <span>{{ item.meta?.title }}</span>
    </el-menu-item>
  </template>
</template>

<script setup>
import { defineProps, computed } from 'vue';

const props = defineProps({
  item: {
    type: Object,
    required: true
  },
  level: {
    type: Number,
    default: 0
  }
});

const hasChildren = computed(() => {
  if (props.level >= 2) {
    if (props.item.children) {
      console.error('菜单层级超过限制,最多允许两层子菜单');
      return false;
    }
    return false;
  }
  return props.item.children && props.item.children.length > 0;
});
</script>

4.修改layout/index.vue

根据AI的代码,使用Sidebar,需要向子组件传入menuData的菜单数组。暂时先根据后端返回的json串,固定返回。
在这里插入图片描述

二.功能验证

运行项目,打开浏览器的开发者工具,如果有报错就需要看代码处理错误。
浏览器访问http://localhost:5173/index
在这里插入图片描述

三.知识点拓展

1. 组件化开发

Vue采用组件化架构,每个.vue文件都是一个独立组件。例如:
SidebarMenu.vue 负责整个侧边栏容器
MenuItem.vue 处理单个菜单项的渲染
• 通过import MenuItem from './MenuItem.vue'实现组件复用

2. 组件通信(Props)

父子组件通过props传递数据:

// 父组件传递数据
<MenuItem :item="item" :level="0" />

// 子组件接收
defineProps({
  item: { type: Object, required: true },
  level: { type: Number, default: 0 }
})

3. 递归组件

组件可以调用自身处理嵌套结构:

<!-- 处理多级菜单 -->
<template v-for="child in item.children">
  <MenuItem :item="child" :level="level + 1" />
</template>

4. 动态组件

通过<component>实现图标动态加载:

<component :is="item.meta.icon" />

根据meta.icon的值动态渲染对应的图标组件

5. 计算属性

使用computed处理逻辑判断:

const hasChildren = computed(() => {
  return props.item.children?.length > 0 && props.level < 2
})

6. 条件渲染

v-if/v-else控制不同状态的显示:

<template v-if="hasChildren">
  <!-- 显示子菜单 -->
</template>
<template v-else>
  <!-- 显示普通菜单项 -->
</template>

7. 列表渲染

v-for指令遍历菜单数据:

<template v-for="item in menuData" :key="item.path">

8. 路由集成

el-menu与Vue Router深度整合:

<el-menu router>
  <el-menu-item index="/user">用户管理</el-menu-item>
</el-menu>

点击菜单自动跳转对应路由路径

9. 组合式API

使用<script setup>语法:

import { defineProps, computed } from 'vue'

简化组件逻辑组织,自动暴露模板变量

10. 组件插槽

使用插槽定制子菜单标题:

<el-sub-menu>
  <template #title>
    <!-- 自定义标题内容 -->
    <el-icon><component :is="icon"/></el-icon>
    <span>{{ title }}</span>
  </template>
</el-sub-menu>

11. UI框架集成

整合Element Plus组件:
<el-menu> 菜单容器
<el-sub-menu> 折叠菜单
<el-menu-item> 菜单项
<el-icon> 图标容器

四.思考

1.为什么菜单点击后,都是返回空白界面?

点击了侧边栏里的用户管理后,跳转到空界面,Layout失效。说好的点击菜单,只有内容区的会变化,现在怎么Layout都不见了。
在这里插入图片描述

2.为什么在Sidebar里定义的是menuData,然后在父组件使用时,可以写成menu-data?

1. 前端领域的「潜规则」

HTML 属性:HTML 规范要求属性名全小写(比如 <div class="xxx">),若强行写驼峰形式 <div menuData="xxx"> 会被当作字符串属性。
JS 属性:JavaScript 习惯用驼峰命名变量(如 menuData),写短横线形式 menu-data 需要加引号,不符合代码审美。

于是 Vue 在中间做了自动翻译

<!-- 父组件模板(HTML 环境) -->
<ChildComponent menu-data="xxx" /> <!-- 这里用短横线 -->

<!-- 自动翻译成 JS 的驼峰形式 -->
{
  menuData: "xxx" // 子组件用驼峰接收
}

2. 为什么要这样设计?

兼容性:HTML 不区分大小写,但 JS 区分,这种转换让两者和谐共处。
开发者友好:你在 JS 中写符合代码规范的驼峰变量,在模板中写符合 HTML 规范的短横线属性,各取所长。


3. 三种等价写法(Vue 都认)
父组件模板写法被子组件接收的 prop 名
menu-data="xxx"menuData
menuData="xxx"menuData (不推荐,违反 HTML 规范)
:menuData="xxx"menuData (动态 prop 时可用,但依然不推荐)

一句话总结:Vue 在底层帮你把 menu-data 翻译成 menuData,这是框架的约定,就像翻译官把中文「你好」自动转成英文「Hello」一样自然。


http://www.kler.cn/a/580475.html

相关文章:

  • Java直通车系列25【Spring Boot】(核心注解)
  • Spring boot 3.3.1 官方文档 中文
  • monaco-editor/react 自定义高亮
  • vue+dhtmlx-gantt 实现甘特图-快速入门【甘特图】
  • 天梯选拔赛赛后补题
  • Unet实战分割项目:深度学习与医学影像分析
  • 大语言模型 (LLM) 基础知识
  • Docker搭建Redis哨兵模式【一主两从三哨兵】
  • 关于统计建模大赛的选题
  • 项目上传到Gitee过程
  • 阿里灵犀互娱游戏界面设计(GUI)岗内推-上海
  • Ae 效果详解:VR 颜色渐变
  • Chrome 扩展开发 API实战:Cookies(一)
  • 前端面试题 口语化复述解答(从2025.3.8 开始频繁更新中)
  • linux wifi driver深度实践之内核编译加载
  • Go语言分布式ID生成策略优选:UUID、Snowflake、XID、ObjectID、Krand性能对比评测
  • 前端杂的学习笔记
  • 字符串习题
  • 实战案例分享:Android WLAN Hal层移植(MTK+QCA6696)
  • 字节跳动C++客户端开发实习生内推-抖音基础技术