vue3.0 + TS 后台管理系统设计(三):菜单角色权限

417 阅读1分钟

关于基础项目搭建以及菜单导航组件可以查看之前的介绍!

路由配置

首先创建一个菜单管理页面来进行菜单管理

const routes = [
  {
    path: "/",
    name: "layout",
    component: Layout,
    meta: { title: "主页" },
  },
  {
    path: "/system",
    name: "system",
    component: Layout,
    meta: { title: "系统管理" },
    children: [
      {
        path: "/menu",
        name: "menu",
        component: () => import("@/views/system/menu/index.vue"),
        meta: { title: "菜单管理" },
      },
    ],
  },
];

此时在 Sidebar 组件中可以通过路由方法获取到整个路由树:

import { useRouter } from "vue-router";
const router = useRouter();
const routers = computed(() => router.options.routes);

同时 Layout 组件需要路由改造一下

<template>
  <el-container>
    <el-aside width="200px">
      <sidebar />
    </el-aside>
    <el-main>
      <RouterView />
    </el-main>
  </el-container>
</template>

此时渲染结果为:

image.png

菜单管理

准备基础页面结构

<!--菜单管理-->
<script setup lang="ts" name="Menu">
enum menuTypeEnum {
  catalog = "1",
  Menu = "2",
  Button = "3",
}

interface MenuType {
  id: string;
  name: string;
  path: string;
  code: string;
  type: menuTypeEnum;
}

const tableData = [];

const onOpenRole = () => {};
const onOpenMenu = () => {};
const onAddClick = (menu: MenuType) => {};
const onEditClick = (menu: MenuType) => {};
const onDeleteClick = (id: string) => {};
</script>

<template>
  <el-container>
    <el-header height="40">
      <el-button @click="onOpenMenu">新增</el-button>
      <el-button @click="onOpenRole">角色授权</el-button>
    </el-header>
    <el-main>
      <el-table border :data="tableData" row-key="id">
        <el-table-column type="selection" align="center" />
        <el-table-column type="index" label="序号" align="center" width="90" />
        <el-table-column label="菜单名称" prop="name" />
        <el-table-column label="菜单路径" align="center" prop="path" />
        <el-table-column label="组件路径" align="center" prop="code" />
        <el-table-column label="菜单类型" align="center" prop="type">
          <template v-slot="scope">
            <span>{{scope.row.type === "1" ? "目录" : scope.row.type === "2" ? "菜单" : "按钮" }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template v-slot="scope">
            <el-button type="text" @click="onAddClick(scope.row)">新增</el-button>
            <el-button type="text" @click="onEditClick(scope.row)">编辑</el-button>
            <el-button type="text" @click="onDeleteClick(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-main>
  </el-container>
</template>

<style scoped lang="scss"></style>

页面显示:

image.png

菜单新增

<el-dialog v-model="isOpenMenu" title="新增/编辑">
  <el-form :model="menuForm" label-width="80px">
    <el-form-item :label="menuForm.type !== '3' ? '菜单名称' : '按钮名称'">
      <el-input v-model="menuForm.name" />
    </el-form-item>
    <el-form-item v-if="menuForm.type !== '3'" label="菜单路径">
      <el-input v-model="menuForm.path" />
    </el-form-item>
    <el-form-item v-if="menuForm.type !== '3'" label="组件路径">
      <el-input v-model="menuForm.code" />
    </el-form-item>
    <el-form-item v-if="menuForm.type === '3'" label="按钮编码">
      <el-input v-model="menuForm.code" />
    </el-form-item>
    <el-form-item label="菜单类型">
      <el-select v-model="menuForm.type">
        <el-option label="目录" :value="menuTypeEnum.Catalog" />
        <el-option label="菜单" :value="menuTypeEnum.Menu" />
        <el-option label="按钮" :value="menuTypeEnum.Button" />
      </el-select>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmitMenu">提交</el-button>
      <el-button @click="onCloseMenu">取消</el-button>
    </el-form-item>
  </el-form>
</el-dialog>
class Menu implements MenuType {
  constructor(data: Partial<MenuType> = {}) {
    this.id = data.id ?? "";
    this.code = data.code ?? "";
    this.name = data.name ?? "";
    this.path = data.path ?? "";
    this.pid = data.pid ?? -1;
    this.type = data.type ?? menuTypeEnum.Catalog;
  }

  code: string;
  id: number | "";
  name: string;
  path: string;
  pid: number;
  type: menuTypeEnum;
}
const menuForm = ref<MenuType>();
const isOpenMenu = ref(false);
const onOpenMenu = () => {
  menuForm.value = new Menu();
  isOpenMenu.value = true;
};
const onSubmitMenu = () => {};
const onCloseMenu = () => {
  isOpenMenu.value = false;
};

image.png

同样子节点新增和编辑:

const onAddClick = (menu: MenuType) => {
  menuForm.value = new Menu({ pid: menu.id as number });
  isOpenMenu.value = true;
};
const onEditClick = (menu: MenuType) => {
  menuForm.value = new Menu(menu);
  isOpenMenu.value = true;
};

json-server

json-server 可以快速帮助我们进行接口 mock

1. 安装依赖

npm install -D json-server

2. 创建 JSON 数据

__json_server_mock__ 文件夹下创建 db.json 文件, 并配置

{
  "system-menu": {}
}

3. 启动命令

package.jsonscripts 命令中配置

"json-server": "json-server ./__json_server_mock__/db.json --port=3669 --watch"

4. 接口封装

import axios from "axios";

const service = axios.create({
  baseURL: "http://localhost:3669/", // url = base url + request url
  timeout: 5000, // request timeout
});

const get = (url: string, params: any) => service.get(url, { params });
const post = (url: string, params: any) => service.post(url, params);
const deletes = (url: string, id: string) => service.delete(`${url}/${id}`);
const put = (url: string, params: { id: string; [k: string]: any }) =>
  service.put(`${url}/${params.id}`, params);

export default {
  get,
  post,
  deletes,
  put,
};

5. 调用接口

import { ref } from "vue";
import server from "@/api/index";
const MENU_URL = "system-menu";

const tableData = ref<MenuType[]>();

const getTableData = () => {
  server.get(MENU_URL).then((res) => {
    tableData.value = res.data;
  });
};

const onDeleteClick = (id: string) => {
  server.deletes(MENU_URL, id).then(() => {
    getTableData();
  });
};

const onSubmitMenu = () => {
  const axiosFn = menuForm.value.id ? server.put : server.post;
  axiosFn(MENU_URL, menuForm.value).then(() => {
    onCloseMenu();
    getTableData();
  });
};

至此实现了对菜单的增删改查