iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0

簡述

其實Menu的控制項取決於登入者的權限。

功能

以Demo來說,所有的頁面種類有以下:

  • index 首頁
  • user 用戶管理
  • admin 權限管理
  • customer 會員管理
  • product 商品管理
  • order 訂單管理

其他前面兩個為開放權限,
也就是不論誰登入都會看到首頁用戶資訊

後面四個權限實際上都會放在 Database
也會紀錄某個人有哪些可以觀看跟操作。

流程

當登入者驗證身分後進來,
前端送token拿登入者User資料,
傳回登入者資料會呈現:

user

而Menu會根據此User的權限,
也就是holds:[...]來渲染畫面。

檔案結構

-src
  |-app
    ...
    |-core
       |-menu
           |-menu.component.css
           |-menu.component.html
           |-menu.component.ts
           |-menu.ts
       ...

實作

menu.ts

export interface IMenu {
  name: string;
  path: string;
  check?: boolean;
}

export const Menu: IMenu[] = [
  { name: "index", path: "/index", check: true },
  { name: "user", path: "/user", check: false }
];

之後拿到此登入者的權限後,Menu: IMenu[]會再加進去。


menu.component.ts

export class MenuComponent implements OnInit {
  LOGOURL = LOGOURL;
  LANG = LANG;
  TOGGLELANG = TOGGLELANG;
  menu: IMenu[] = Menu;
  isDevice = "";

  constructor(
    private coreService: CoreService,
    private breakpointObserver: BreakpointObserver,
    private tabService: TabService,
    private dataService: DataService,
    private userService: UserService
  ) {}

  ngOnInit() {
    this.breakpointObserver.observe("(max-width: 1199px)")
    .subscribe(r => {
      this.isDevice = r.matches ? "mb" : "pc";
      this.filterMenu();
    });
    this.setMenu();
  }

  setMenu() {
    let url = this.dataService.setUrl("powers");
    this.dataService.getData(url).subscribe((data: IData) => {
      if (!!data && !!data.res) {
        let powers = <IPower[]>data.res;

        powers.forEach((power: IPower) => {
          this.userService.getUser().holds.forEach((hold: IHold) => {
            if (hold.powerId === power.id) {
              let obj = <IMenu>{
                name: `${power.name}`,
                path: `/${power.name}`,
                check: false
              };
              this.menu.push(obj);
            }
          });
        });
      }
    });
    this.filterMenu();
  }

  filterMenu() {
    let index = this.menu
      .map(item => {
        return item.name;
      })
      .indexOf("user");
    if (this.isDevice === "pc" && index !== -1) {
      this.menu.splice(index, 1);
    }
    if (this.isDevice === "mb" && index === -1) {
      this.menu.splice(1, 0, {
        name: "user",
        path: "/user",
        check: false
      });
    }
  }

  toggleMenuInfo(menuInfo: IMenu) {
    menuInfo.check = !menuInfo.check;
    this.setListTab(menuInfo);
  }

  setListTab(menuInfo: IMenu) {
    this.coreService.nextHamburger("next");
    let tab = <ITabBase>{
      tag: `${menuInfo.name}_list`,
      path: "cms" + menuInfo.path
    };
    let tabMain = <ITabMain>{
      request: "insert",
      content: tab
    };
    this.tabService.nextTabMain(tabMain);
  }

  logout() {
    this.coreService.logout();
  }

  useLanguage($event?) {
    if (!!$event) {
      this.coreService.useLanguage($event);
    }
  }

  getNowLang() {
    return this.coreService.getNowLang();
  }
}
  • setMenu():加入此使用者權限已便完成 menu: IMenu[]

  • filterMenu():根據目前的螢幕尺寸,
    來判斷 多語系用戶資訊 是否要放在Menu裡,
    mb版 的右上角多語系跟用戶資訊會消失。

  • toggleMenuInfo():點擊了頁面大標題後,
    會觸發 setListTab() 會讓右邊出現功能頁,
    Tab這部分之後會提及。

此Demo比較小,很多情況應該都會有次選單。

其實Menu複雜度要看專案大小,
比較龐大的系統,不僅僅是所有頁面控制項,
恐怕連list裡的按鈕都會有權限控制。


menu.component.html

<div class="side-box">
  <div class="side-container">
    <div class="logo" 
    [style.background-image]="'url(' + LOGOURL + ')'">
    </div>
    <div class="menu" *ngIf="!!menu">
      <div class="menu-items">
        <ng-container *ngFor="let m of menu">
          <div class="menu-item" (click)="toggleMenuInfo(m)">
            <div class="icon" [ngSwitch]="m.name">
              <mat-icon *ngSwitchCase="'index'" svgIcon="index">
              </mat-icon>
              <mat-icon *ngSwitchCase="'user'" svgIcon="user">
              </mat-icon>
              <mat-icon *ngSwitchCase="'admin'" svgIcon="admin">
              </mat-icon>
              <mat-icon *ngSwitchCase="'customer'" svgIcon="customer">
              </mat-icon>
              <mat-icon *ngSwitchCase="'product'" svgIcon="product">
              </mat-icon>
              <mat-icon *ngSwitchCase="'order'" svgIcon="order">
              </mat-icon>
            </div>
            <div class="content">
              <span class="b">
                {{ m.name + "_manage" | translate }}
              </span>
            </div>
          </div>
        </ng-container>
      </div>
    </div>
    <div class="menu-other">
      <div class="lang" *ngIf="TOGGLELANG && isDevice === 'mb'">
        <div class="menu-item">
          <div class="icon">
            <mat-icon svgIcon="global" (click)="select.open()">
            </mat-icon>
          </div>
          <div class="content">
            <mat-select
              #select
              [value]="getNowLang()"
              (valueChange)="useLanguage($event)"
              [panelClass]="'font-white'"
              class="b"
            >
              <mat-option *ngFor="let lang of LANG" 
              [value]="lang.short" class="b">
                {{lang.long | translate}}
              </mat-option>
            </mat-select>
          </div>
        </div>
      </div>
      <div class="logout" (click)="logout()">
        <div class="menu-item">
          <div class="icon">
            <mat-icon>exit_to_app</mat-icon>
          </div>
          <div class="content">
            <span class="b">{{ "logout" | translate }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

menu.component.css

樣式請參照下方#範例網。


範例碼

https://stackblitz.com/edit/ngcms-corecomp

Start

一開始會跳出提示視窗顯示fail為正常,
請先從範例專案裡下載或是複製db.json到本地端,
並下指令:

json-server db.json

json-server開啟成功後請連結此網址:
https://ngcms-corecomp.stackblitz.io/cms?token=bc6e113d26ce620066237d5e43f14690


上一篇
day12 CoreComponent
下一篇
day14 TabModule
系列文
用Angular打造完整後台30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言