前言

本文要分享的是一个多级菜单效果,也就是传说中的树形结构菜单,理论上支持无限级菜单,当然数据结构要一定的要求,但这都不是什么难事,因为我们可以把数据组装成所需要的结构。下面这个例子虽然不是很完美好,但是估计也够用了。这个多级菜单是模仿 Angular 官方的左侧菜单效果来做的,效果的相似度应该达到 99%,本文内容有点多(主要是代码),因为我想把所有的代码都贴出来,尽量不让你幻想缺失的代码。好了,下面我们就开始这个菜单功能之旅吧!

这个多级菜单实现的功能如下:

  • 1、可展示多级菜单,理论上可以展无限级菜单
  • 2、当前菜单高亮功能
  • 3、刷新后依然会自动定位到上一次点击的菜单,即使这个是子菜单,并且父菜单会自动展开
  • 4、子菜单的显示隐藏有收起、展开,同时带有淡入淡出效果

Angular 多级菜单

还是老套路,费放不多说,我们直接上码。在上码前,我们不妨先看看代码文件结构概览图:

效果图看完之后,我们再来看看效果图:毕竟这是能让你有勇气把下面的一大堆代码阅读完的动力来源:

展开【教程】菜单再点【英雄编辑器菜单】,接着再点击【核心知识】-【模块与数据绑定】-【生命周期勾子】,然后刷新页面,菜单就会自动定位到【生命周期勾子】菜单并高亮,并且【核心知识】-【模块与数据绑定】菜单会自动展开并高亮。

上面点击每个菜单时都会跳转到一个空白的详情页,但这个详情页什么都没做,只是为了保证菜单能正常跳转而已,你可以通过观察导航栏中的 URL 变化来确定菜单是否已经跳转成功。

首先把最主要的代码贴出来:

navItem.component.html
  1. <div class="level-1">
  2. <ng-template [ngIf]="menu.type === 'link'">
  3. <div>
  4. <a class="link level-1" routerLink="{{menu.url}}" routerLinkActive="selected" (click)="toggleSubMenu(menu)">{{ menu.name }}</a>
  5. </div>
  6. </ng-template>
  7. <ng-template [ngIf]="menu.type === 'button'">
  8. <div>
  9. <div class="button heading" [ngClass]="{expand:menu.expand,selected:menu.isSelected}" (click)="toggleSubMenu(menu)">
  10. {{menu.name}}
  11. <div class="icon">
  12. <svg xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 24 24">
  13. <path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path>
  14. </svg>
  15. </div>
  16. </div>
  17. <ng-template [ngIf]="menu.expand">
  18. <div class="heading-children" [@inOut]="out">
  19. <nav-item [menu]="menu" *ngFor="let menu of menu.subMenu"></nav-item>
  20. </div>
  21. </ng-template>
  22. </div>
  23. </ng-template>
  24. </div>

上面的 routerLinkActive 可以设置当前菜单高亮。[@inOut] 为绑定的动画效果,具体用法可以参考官方资料。

这个 html 中使用了 Angular 中的一个标签 <ng-template> 关于这个标签的用法可以网上搜索一下资料。

navItem.component.css
  1. a {
  2. text-decoration: none;
  3. color: #333;
  4. }
  5. .link,
  6. .button {
  7. display: block;
  8. padding: 10px 15px;
  9. transition: background-color 0.2s ease-in-out 0s, color 0.3s ease-in-out 0.1s;
  10. -moz-user-select: none;
  11. -webkit-user-select: none;
  12. -ms-user-select: none;
  13. -khtml-user-select: none;
  14. user-select: none;
  15. }
  16. .button {
  17. position: relative;
  18. }
  19. .link:hover,
  20. .button:hover {
  21. color: #1976d2;
  22. background-color: #eee;
  23. cursor: pointer;
  24. }
  25. .icon {
  26. position: absolute;
  27. right: 0;
  28. display: inline-block;
  29. height: 24px;
  30. width: 24px;
  31. fill: currentColor;
  32. transition: -webkit-transform .15s;
  33. transition: transform .15s;
  34. transition: transform .15s, -webkit-transform .15s;
  35. transition-timing-function: ease-in-out;
  36. }
  37. .heading-children {
  38. padding-left: 14px;
  39. overflow: hidden;
  40. }
  41. .expand {
  42. display: block;
  43. }
  44. .collapsed {
  45. display: none;
  46. }
  47. .expand .icon {
  48. -webkit-transform: rotate(90deg);
  49. transform: rotate(90deg);
  50. }
  51. .selected {
  52. color: #1976d2;
  53. }
navItem.component.ts
  1. import { Component, Input, OnInit } from '@angular/core';
  2. import { ActivatedRoute, Router } from '@angular/router';
  3. import { trigger, state, style, animate, transition } from '@angular/animations';
  4. import { MenusService } from '../services/menus.services.component';
  5. @Component({
  6. selector: 'nav-item',
  7. templateUrl: './navItem.component.html',
  8. styleUrls: ['./navItem.component.css'],
  9. animations: [
  10. trigger('inOut', [
  11. state('out', style({ opacity: 0, height: 0 })),
  12. transition('void => *', [
  13. style({ opacity: 0, height: 0 }),
  14. animate(150, style({ opacity: 1, height: '*' }))
  15. ]),
  16. transition('* => void', [
  17. style({ opacity: 1, height: '*' }),
  18. animate(150, style({ opacity: 0, height: 0 }))
  19. ])
  20. ])
  21. ]
  22. })
  23. export class SideItemComponent implements OnInit {
  24. startExpand = []; // 保存刷新后当前要展开的菜单项
  25. targetUrl = ""; // 保存目标 URL,即当前 url,通过它来定位当前菜单高亮
  26. source = [];
  27. sourceItem = "";
  28. @Input() menu; // 接收父组件传入的值
  29. constructor(
  30. private _router: Router,
  31. private _activatedRoute: ActivatedRoute,
  32. private _MenusService: MenusService
  33. ) { }
  34. ngOnInit() {
  35. this._MenusService.getMenu().then(data => {
  36. this.source = data;
  37. this.setCurrentMenu();
  38. });
  39. }
  40. // 展开并设置当前菜单高度
  41. setCurrentMenu() {
  42. // console.log(this._router);
  43. this.targetUrl = this._router.url; // 获取当前url
  44. this.targetUrl = this.targetUrl.substr(1, this.targetUrl.length); // 处理获取的 url, 即截掉 url 前的 “ /”
  45. this.setExpand(this.source);
  46. }
  47. setExpand(source) {
  48. for (var i = 0; i < source.length; i++) {
  49. this.sourceItem = JSON.stringify(source[i]); // 把菜单项转为字符串
  50. if (this.sourceItem.indexOf(this.targetUrl) > -1) { // 查找当前 URL 所对应的子菜单属于哪一个祖先菜单
  51. if (source[i].type === 'button') { // 一级导航为展开按钮
  52. this.startExpand.push(source[i]);
  53. source[i].isSelected = true;
  54. source[i].expand = true; // 设置为展开
  55. // 递归下一级菜单,以此类推
  56. this.setExpand(source[i].subMenu);
  57. }
  58. break;
  59. }
  60. }
  61. }
  62. toggleSubMenu(menuItem) {
  63. if (menuItem.type === 'link') {
  64. // 去掉刷新后展开菜单的高亮(如果有的话)
  65. if (this.startExpand.length > 0) {
  66. for (var i = 0; i < this.startExpand.length; i++) {
  67. delete this.startExpand[i].isSelected;
  68. }
  69. }
  70. this.targetUrl = menuItem.url;
  71. this.setExpand(this.source);
  72. this.startExpand = [];
  73. }
  74. menuItem.expand = !menuItem.expand;
  75. }
  76. }

通过 Router 的 url 属性拿到当前的 url,然后在遍历菜单对象的每一项(把它转为字符串),然后查找当前的这个 url 存在哪一个菜单菜单中。

上面的代码通过递归组件的方法来实现菜单的多级显示功能。

接下来我们就在 navMenu 组件中引入这个组件:

navMenu.component.html
  1. <div class="side-nav-box">
  2. <nav-item [menu]="menu" *ngFor="let menu of menus"></nav-item>
  3. </div>
navMenu.component.css
  1. .side-nav-box {
  2. width: 300px;
  3. max-height: 100%;
  4. overflow-y: auto;
  5. overflow-x: hidden;
  6. font-size: 14px;
  7. }
navMenu.component.ts
  1. import { Component, OnInit } from '@angular/core';
  2. import { MenusService } from '../services/menus.services.component';
  3. @Component({
  4. selector: 'nav-menu',
  5. templateUrl: './navMenu.component.html',
  6. styleUrls: ['./navMenu.component.css']
  7. })
  8. export class NavMenuComponent implements OnInit {
  9. menus = [];
  10. constructor(
  11. private _menusService: MenusService
  12. ) { }
  13. ngOnInit() {
  14. this._menusService.getMenu().then(data => {
  15. this.menus = data;
  16. });
  17. }
  18. }

接下来我们在 navSide.component.ts 中引入 navMenu 组件:

navSide.component.html
  1. <nav-menu></nav-menu>
navSide.component.ts
  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'nav-side',
  4. templateUrl: './navSide.component.html',
  5. styleUrls: ['./navSide.component.css']
  6. })
  7. export class NavSideComponent { }

上面就是一个完整的多级菜单组件。下面我们就把这个组件引入 app.component.ts 组件中:

app.component.html
  1. <nav-side></nav-side>
  2. <router-outlet></router-outlet>
app.component.ts
  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'app-root',
  4. templateUrl: './app.component.html',
  5. styleUrls: ['./app.component.css']
  6. })
  7. export class AppComponent {
  8. title = 'app';
  9. }

为了让这个例子可以运行起来,我还为它准备了一些菜单数据,和简单的路由跳转:

menus-mock.ts
  1. export const MENUS = [
  2. { name: '快速上手', type: "link", url: "detail/quickstart" },
  3. {
  4. name: '教程',
  5. type: "button",
  6. expand: false,
  7. subMenu: [
  8. { name: '简介', type: "link", url: "detail/tutorial" },
  9. { name: '英雄编辑器', type: "link", url: "detail/toh-pt1" },
  10. { name: '主从结构', type: "link", url: "detail/toh-pt2" },
  11. { name: '多个组件', type: "link", url: "detail/toh-pt3" },
  12. { name: '服务', type: "link", url: "detail/toh-pt4" },
  13. { name: '路由', type: "link", url: "detail/toh-pt5" },
  14. { name: 'HTTP', type: "link", url: "detail/toh-pt6" },
  15. ]
  16. },
  17. {
  18. name: '核心知识',
  19. type: "button",
  20. expand: false,
  21. subMenu: [
  22. { name: '架构', type: "link", url: "detail/architecture" },
  23. {
  24. name: '模板与数据绑定',
  25. type: "button",
  26. expand: false,
  27. subMenu: [
  28. { name: '显示数据', type: "link", url: "detail/displaying-data" },
  29. { name: '模板语法', type: "link", url: "detail/template-syntax" },
  30. { name: '生命周期钩子', type: "link", url: "detail/lifecycle-hooks" },
  31. { name: '组件交互', type: "link", url: "detail/component-interaction" },
  32. { name: '组件样式', type: "link", url: "detail/component-styles" },
  33. { name: '动态组件', type: "link", url: "detail/dynamic-component-loader" },
  34. { name: '属性型指令', type: "link", url: "detail/attribute-directives" },
  35. { name: '结构型指令', type: "link", url: "detail/structural-directives" },
  36. { name: '管道', type: "link", url: "detail/pipes" },
  37. { name: '动画', type: "link", url: "detail/animations" },
  38. ]
  39. },
  40. {
  41. name: '表单',
  42. type: "button",
  43. expand: false,
  44. subMenu: [
  45. { name: '用户输入', type: "link", url: "detail/user-input" },
  46. { name: '模板驱动表单', type: "link", url: "detail/forms" },
  47. { name: '表单验证', type: "link", url: "detail/form-validation" },
  48. { name: '响应式表单', type: "link", url: "detail/reactive-forms" },
  49. { name: '动态表单', type: "link", url: "detail/dynamic-form" }
  50. ]
  51. },
  52. { name: '引用启动', type: "link", url: "detail/bootstrapping" },
  53. {
  54. name: 'NgModules',
  55. type: "button",
  56. expand: false,
  57. subMenu: [
  58. { name: 'NgModule', type: "link", url: "detail/ngmodule" },
  59. { name: 'NgModule 常见问题', type: "link", url: "detail/ngmodule-faq" }
  60. ]
  61. },
  62. {
  63. name: '依赖注入',
  64. type: "button",
  65. expand: false,
  66. subMenu: [
  67. { name: '依赖注入', type: "link", url: "detail/dependency-injection" },
  68. { name: '多级注入器', type: "link", url: "detail/hierarchical-dependency-injection" },
  69. { name: 'DI 实例技巧', type: "link", url: "detail/dependency-injection-in-action" }
  70. ]
  71. },
  72. { name: 'HttpClient', type: "link", url: "detail/http" },
  73. { name: '路由与导航', type: "link", url: "detail/router" },
  74. { name: '测试', type: "link", url: "detail/testing" },
  75. { name: '速查表', type: "link", url: "detail/cheatsheet" },
  76. ]
  77. },
  78. {
  79. name: '其它技术',
  80. type: "button",
  81. expand: false,
  82. subMenu: [
  83. { name: '国际化(i18n)', type: "link", url: "detail/i18n" },
  84. { name: '语言服务', type: "link", url: "detail/language-service" },
  85. { name: '安全', type: "link", url: "detail/security" },
  86. {
  87. name: '环境设置与部署',
  88. type: "button",
  89. expand: false,
  90. subMenu: [
  91. { name: '搭建本地开发环境', type: "link", url: "detail/setup" },
  92. { name: '搭建方式剖析', type: "link", url: "detail/setup-systemjs-anatomy" },
  93. { name: '浏览器支持', type: "link", url: "detail/browser-support" },
  94. { name: 'npm 包', type: "link", url: "detail/npm-packages" },
  95. { name: 'TypeScript 配置', type: "link", url: "detail/typescript-configuration" },
  96. { name: '预 (AoT) 编译器', type: "link", url: "detail/aot-compiler" },
  97. { name: '预 (AoT) 编译器', type: "link", url: "detail/metadata" },
  98. { name: '部署', type: "link", url: "detail/deployment" }
  99. ]
  100. },
  101. {
  102. name: '升级',
  103. type: "button",
  104. expand: false,
  105. subMenu: [
  106. { name: '从 AngularJS 升级', type: "link", url: "detail/upgrade" },
  107. { name: '升级速查表', type: "link", url: "detail/ajs-quick-reference" }
  108. ]
  109. },
  110. { name: 'Visual Studio 2015 快速上手', type: "link", url: "detail/visual-studio-2015" },
  111. { name: '风格指南', type: "link", url: "detail/styleguide" },
  112. { name: '词汇表', type: "link", url: "detail/glossary" }
  113. ]
  114. },
  115. { name: 'API 参考手册', type: "link", url: "detail/api" }
  116. ];

上面数据中的 expand: false, 其实也可以不要的,因为如果对象中不存在 expand 属性,则是 false 。即默认收起所有菜单。我们在程序中可以动态给它添加。当然在实践的开发中菜单数据结构可能会更复杂,对象属性更多,但万变不离其宗。

在这里我们还可以把这个菜单对象使用平铺式的数据结构(即,不管是子菜单还是父菜单,都同放在一个数据里)来做,而不用像上面那样父菜单嵌套着子菜单

接着,通过服务来返回这些数据:

menus.services.ts
  1. import { Injectable } from '@angular/core';
  2. import { MENUS } from '../services/menus-mock';
  3. @Injectable()
  4. export class MenusService {
  5. getMenu(): Promise<any[]> {
  6. return Promise.resolve(MENUS);
  7. }
  8. }

加一个简单得不能再简单的详情页,用来方便点击菜单时作跳转,这里只做了一个页面,所有的菜单都会跳到这个页面:

detail.component.ts
  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'detail-page',
  4. templateUrl: './detail.component.html',
  5. styleUrls: ['./detail.component.css']
  6. })
  7. export class detailComponent {}

下面就是给出所以菜单的路由:

navRouter.module.ts
  1. import { NgModule } from '@angular/core';
  2. import { RouterModule, Routes } from '@angular/router';
  3. import { detailComponent } from './detail/detail.component';
  4. const appRoutes: Routes = [
  5. { path: 'detail/quickstart', component: detailComponent },
  6. { path: 'detail/tutorial', component: detailComponent },
  7. { path: 'detail/toh-pt1', component: detailComponent },
  8. { path: 'detail/toh-pt2', component: detailComponent },
  9. { path: 'detail/toh-pt3', component: detailComponent },
  10. { path: 'detail/toh-pt4', component: detailComponent },
  11. { path: 'detail/toh-pt5', component: detailComponent },
  12. { path: 'detail/toh-pt6', component: detailComponent },
  13. { path: 'detail/architecture', component: detailComponent },
  14. { path: 'detail/displaying-data', component: detailComponent },
  15. { path: 'detail/template-syntax', component: detailComponent },
  16. { path: 'detail/lifecycle-hooks', component: detailComponent },
  17. { path: 'detail/component-interaction', component: detailComponent },
  18. { path: 'detail/component-styles', component: detailComponent },
  19. { path: 'detail/dynamic-component-loader', component: detailComponent },
  20. { path: 'detail/attribute-directives', component: detailComponent },
  21. { path: 'detail/structural-directives', component: detailComponent },
  22. { path: 'detail/pipes', component: detailComponent },
  23. { path: 'detail/animations', component: detailComponent },
  24. { path: 'detail/user-input', component: detailComponent },
  25. { path: 'detail/forms', component: detailComponent },
  26. { path: 'detail/form-validation', component: detailComponent },
  27. { path: 'detail/reactive-forms', component: detailComponent },
  28. { path: 'detail/dynamic-form', component: detailComponent },
  29. { path: 'detail/bootstrapping', component: detailComponent },
  30. { path: 'detail/ngmodule', component: detailComponent },
  31. { path: 'detail/ngmodule-faq', component: detailComponent },
  32. { path: 'detail/dependency-injection', component: detailComponent },
  33. { path: 'detail/hierarchical-dependency-injection', component: detailComponent },
  34. { path: 'detail/dependency-injection-in-action', component: detailComponent },
  35. { path: 'detail/http', component: detailComponent },
  36. { path: 'detail/router', component: detailComponent },
  37. { path: 'detail/testing', component: detailComponent },
  38. { path: 'detail/cheatsheet', component: detailComponent },
  39. { path: 'detail/i18n', component: detailComponent },
  40. { path: 'detail/language-service', component: detailComponent },
  41. { path: 'detail/security', component: detailComponent },
  42. { path: 'detail/setup', component: detailComponent },
  43. { path: 'detail/setup-systemjs-anatomy', component: detailComponent },
  44. { path: 'detail/browser-support', component: detailComponent },
  45. { path: 'detail/npm-packages', component: detailComponent },
  46. { path: 'detail/typescript-configuration', component: detailComponent },
  47. { path: 'detail/aot-compiler', component: detailComponent },
  48. { path: 'detail/metadata', component: detailComponent },
  49. { path: 'detail/deployment', component: detailComponent },
  50. { path: 'detail/upgrade', component: detailComponent },
  51. { path: 'detail/ajs-quick-reference', component: detailComponent },
  52. { path: 'detail/visual-studio-2015', component: detailComponent },
  53. { path: 'detail/styleguide', component: detailComponent },
  54. { path: 'detail/glossary', component: detailComponent },
  55. { path: 'detail/api', component: detailComponent }
  56. ];
  57. @NgModule({
  58. imports: [
  59. RouterModule.forRoot(appRoutes)
  60. ],
  61. exports: [
  62. RouterModule
  63. ]
  64. })
  65. export class AppRoutesModule { }

这里我们把路由单独成一个模块,所有的菜单都会跳转到同一个详情页中,只不过每个菜单都有自己单独的路由。

接下来就是最后一步,也是最关键的一步了,那就是在app.component.ts 中引入上面这些资源:

app.module.ts
  1. import { BrowserModule } from '@angular/platform-browser';
  2. import { NgModule } from '@angular/core';
  3. import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
  4. import { RouterModule } from '@angular/router';
  5. import { AppRoutesModule } from './nav.routes.module';
  6. import { AppComponent } from './app.component';
  7. import { NavSideComponent } from './sidenav/navSide.component';
  8. import { NavMenuComponent } from './sidenav/navMenu.component';
  9. import { SideItemComponent } from './sidenav/navItem.component';
  10. import { detailComponent } from './detail/detail.component';
  11. import { MenusService } from './services/menus.services.component';
  12. @NgModule({
  13. declarations: [
  14. AppComponent,
  15. NavSideComponent,
  16. NavMenuComponent,
  17. SideItemComponent,
  18. detailComponent
  19. ],
  20. imports: [
  21. BrowserModule,
  22. BrowserAnimationsModule,
  23. AppRoutesModule
  24. ],
  25. providers: [MenusService],
  26. bootstrap: [AppComponent]
  27. })
  28. export class AppModule { }

到这里这个用Angular 实现的多级菜单就已经完成了。不要看代码那么多,其实真正关键的代码非常地少。

最后,想说说这个 Angular 功能模块一个缺点:

由于使用了递归组件的方式来自动判断生成菜单,所以把调用一次组件都会生成一个组件实例,比如,我们第一次进行到这个菜单页面,当前有 5 个菜单,那么就会生成 5 个实例,这样导致的问题是,每个组件的 ngOnInit 函数都会执行一遍。这就相当于 ngOnInit 函数里的代码都会执行5次,如果你点开了一些子菜单,那么就会生成更多的实例,ngOnInit 函数里的代码就会执行更多次。不过这个对于一般的菜单来说也不是什么大问题。

有什么不懂,或者对于上面这个功能模块有什么好的改进意见的可以随时留言,一起交流探讨,我觉得交流分享是最好的提升自身能力的方式之一。

Angular 2+ 实现多级菜单功能模块(树形结构菜单)就分享到这里。

Angular 实现树形菜单(多级菜单)功能模块相关推荐

  1. STM32简易多级菜单(数组查表法)

    单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了. 1 多级菜单 多级菜单的实现,大体分为两种设计思路: 通过双向链表实现 通过数组查表实现 总体思路都是把菜单 ...

  2. vant树型菜单多级_iOS 动态树形结构 - 实现多级菜单,附带复选框功能

    关键词:递归 多级菜单 复选 目标: 1.显示多级菜单,默认显示一级. 2.可以通过点击有子级的行展开菜单 3.通过复选框,改变选中状态.状态有全选.半选.未选中 4.可以扩展获取当前所选的条目集合 ...

  3. java递归实现多级菜单栏_Java构建树形菜单以及支持多级菜单的实例代码

    这篇文章主要介绍了Java构建树形菜单的实例代码(支持多级菜单),非常不错,具有参考借鉴价值,需要的朋友可以参考下 效果图:支持多级菜单. 菜单实体类: public class Menu { // ...

  4. html jquery 菜单选中,jQuery树形菜单插件多级菜单选中代码

    特效描述:jQuery 树形菜单插件 多级菜单选中.jQuery树形菜单插件,点击展开多级下拉菜单,点击选中菜单效果代码. 代码结构 1. 引入JS 2. HTML代码 打开 | 关闭 d = new ...

  5. java构建树形菜单(多级菜单)

    一.树形结构简介 首先构建树形结构,我们得知道什么是树形结构,如下图所示. 如上图所示,可以看出有四级菜单,这就是树形结构. 如果想更深入的了解树形结构,建议去查询数据结构中关于树的章节. 二.树形菜 ...

  6. 基于STM32的OLED多级菜单GUI实现(简化版智能手表)

    前言:本文的OLED多级菜单UI为一个综合性的STM32小项目,使用多传感器与OLED显示屏实现智能终端的效果.项目中的多级菜单UI使用了较为常见的结构体索引法去实现功能与功能之间的来回切换,搭配DH ...

  7. 基于链表的多级菜单设计

    基于链表的多级菜单设计 前言 主体 前言 最近在做一个简单的界面,需要用到多级菜单,一开始使用的是传统的索引法,在修改时比较乱,在网上有用链表写的,虽然一致都在用c语言,却很少使用链表,于是今天早上便 ...

  8. 【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

    需求 1.检测参数:水温.TDS.浊度.PH 2.超出阈值声光报警 3.LCD显示目标参数的测量结果 4.测量模式:单参数测量.所有参数表同时测量 切换方式:按键切换 原理 单总线技术 单总线技术采用 ...

  9. mysql vue 菜谱_vue+ java 实现多级菜单递归效果

    效果如图: 大概思路:树形视图使用的是vue官方事例代码,java负责封装数据,按照vue官方事例的数据结构封装数据即可.有两个需要关注的点: 1.官方事例的数据结构是一个对象里面包含着集合,而不是一 ...

最新文章

  1. python实现 “不带中括号”输出列表中的数据
  2. NBT:主流高通量测序仪在人/细菌/宏基因组测序评测结果发布,华大智造表现优异...
  3. vue 数组长度_深入理解Vue的数据响应式
  4. jQuery相当于对 javascript二次开发,所以基于 jQuery实现的各种插件直接调用即可...
  5. 如何在 NET 程序万种死法中有效的生成 Dump (下)
  6. uva 10110——Light, more light
  7. UI实用素材|字体在设计中的重要性
  8. asp.net 小记
  9. Spring学习总结(22)——spring-framework-bom解决spring的不同模块依赖版本不同问题...
  10. 我想学习编程,是自学好还是去培训机构学好呢?
  11. OpenCV Laplacian 拉普拉斯变换
  12. mpvue返回上一个页面_服务器出现404页面是什么情况了
  13. 山重水复 Thinkpad T61改装记
  14. Unity3d - 打飞碟
  15. java4android网易云_仿照网易云音乐界面 android特效
  16. 动词变名词的变化规则_英语单词词性转换规则大全
  17. RabbitMQ实现即时通讯
  18. fanc 机器人_24个FANUC机器人的视觉功能详细介绍
  19. 北京地铁月度消费总金额计算(Python版)
  20. STM32F4的DMA

热门文章

  1. 纸牌游戏扎金花的牌大小比较(PHP)
  2. 比较清晰的求马鞍点方法,不过浪费空间时间。
  3. 大数据精准投放平台_大数据精准投放,让你摆脱千篇一律的广告投放!
  4. 都问我在阿里上班是什么体验?今天就闲聊一下在阿里上班的体验!
  5. 【人物访谈·01期】关于对数据分析前辈的一次行业探讨
  6. shell中用grep查找并且不输出_Linux中用 grep查找特定进程,屏蔽grep进程本身(也就是不出现grep --color=auto)...
  7. oracle万能分页代码,Oracle万能分页法的存储过程
  8. 618有什么运动装备推荐、好用性价比高的运动装备合集
  9. 爱普生几种低功耗时钟芯片
  10. 无源波分和彩光模块_波分光模块详细介绍