image.png
点击菜单👉编辑
image.png
新增
image.png
删除
image.png

image.png

2-1 菜单路由

src/routes/access.ts

  1. import Router from '@koa/router'
  2. import {
  3. addAccessController,
  4. getAccessAllController,
  5. removeAccessController,
  6. updateAccessController,
  7. updateBulkAccessController
  8. } from '../controller/access'
  9. const router = new Router({
  10. prefix: '/api/access'
  11. })
  12. /**
  13. * 添加菜单
  14. * post /api/access/menu
  15. */
  16. router.post('/menu', async (ctx) => {
  17. ctx.body = await addAccessController(ctx.request.body)
  18. })
  19. /**
  20. * 获取菜单
  21. * get /api/access/menu
  22. */
  23. router.get('/menus', async (ctx) => {
  24. ctx.body = await getAccessAllController()
  25. })
  26. /**
  27. * 删除某一个菜单
  28. * delete /api/access/menu
  29. */
  30. router.delete('/menu/:id', async (ctx) => {
  31. const { id } = ctx.params
  32. ctx.body = await removeAccessController(Number(id))
  33. })
  34. /**
  35. * 编辑某一个菜单
  36. * put /api/access/menu/:id
  37. */
  38. router.put('/menu/:id', async (ctx) => {
  39. const { id } = ctx.params
  40. ctx.body = await updateAccessController(Number(id), ctx.request.body)
  41. })
  42. /**
  43. * 批量更新
  44. * patch /api/access/menu/update
  45. */
  46. router.patch('/menu/update', async (ctx) => {
  47. const { access } = ctx.request.body
  48. ctx.body = await updateBulkAccessController(access)
  49. })
  50. export default router

2-2 菜单controller

src/controller/access.ts

  1. import { createAccess, getAllAccess, removeAccessById, updateAccessById, updateBulkAccess } from '../services/access'
  2. import { createErrorResponse, SuccessResponse } from '../utils/Response'
  3. import errorInfo from '../constants/errorInfo'
  4. import { AccessModelProps } from '../db/models/access'
  5. const {
  6. addAccessFailInfo,
  7. getAccessAllFailInfo,
  8. removeAccessFailInfo,
  9. updateAccessFailInfo
  10. } = errorInfo
  11. export const addAccessController = async (params: AccessModelProps) => {
  12. if (params) {
  13. try {
  14. const result = await createAccess({
  15. ...params
  16. })
  17. return new SuccessResponse(result)
  18. } catch (error) {
  19. console.error(error.message)
  20. return createErrorResponse(addAccessFailInfo)
  21. }
  22. }
  23. }
  24. export const getAccessAllController = async () => {
  25. try {
  26. const result = await getAllAccess()
  27. return new SuccessResponse(result)
  28. } catch (error) {
  29. console.error(error.message)
  30. return createErrorResponse(getAccessAllFailInfo)
  31. }
  32. }
  33. export const removeAccessController = async (id: number) => {
  34. try {
  35. await removeAccessById(id)
  36. return new SuccessResponse(null, '删除成功!')
  37. } catch (error) {
  38. console.error(error.message)
  39. return createErrorResponse(removeAccessFailInfo)
  40. }
  41. }
  42. export const updateAccessController = async (id: number, data: AccessModelProps) => {
  43. try {
  44. await updateAccessById(id, data)
  45. return new SuccessResponse(null, '菜单更新成功!')
  46. } catch (error) {
  47. console.error(error.message)
  48. return createErrorResponse(updateAccessFailInfo)
  49. }
  50. }
  51. export const updateBulkAccessController = async (data: AccessModelProps[]) => {
  52. try {
  53. await updateBulkAccess(data)
  54. return new SuccessResponse(null, '菜单更新成功!')
  55. } catch (error) {
  56. console.error(error.message)
  57. return createErrorResponse(updateAccessFailInfo)
  58. }
  59. }

2-3 菜单services

src/services/access.ts

  1. import Sequelize from 'sequelize'
  2. import AccessModel, { AccessModelProps } from '../db/models/access'
  3. // Sequelize 操作对象 里面包含了 多条件操作 and or in get等
  4. // 操作符参考文档
  5. // https://www.sequelize.com.cn/core-concepts/model-querying-basics#%E5%BA%94%E7%94%A8-where-%E5%AD%90%E5%8F%A5
  6. // https://www.sequelize.com.cn/core-concepts/model-querying-basics#%E6%93%8D%E4%BD%9C%E7%AC%A6
  7. const OP = Sequelize.Op
  8. // 创建菜单资源
  9. export const createAccess = async (params: AccessModelProps) => {
  10. const result = await AccessModel.create({
  11. ...params
  12. })
  13. return result.toJSON()
  14. }
  15. // 获取所有菜单资源
  16. export const getAllAccess = async () => {
  17. const result = await AccessModel.findAll({
  18. order: [
  19. ['sort_id', 'ASC']
  20. ]
  21. })
  22. return result
  23. }
  24. // 通过id删除菜单 包括parentId为该id的菜单
  25. export const removeAccessById = async (id: number) => {
  26. const result = await AccessModel.destroy({
  27. where: {
  28. [OP.or]: [ // id=1 or parent_id=1
  29. { id },
  30. { parent_id: id }
  31. ]
  32. }
  33. })
  34. return result
  35. }
  36. // 编辑菜单
  37. export const updateAccessById = async (id: number, data: AccessModelProps) => {
  38. const { title, name, path, icon } = data
  39. const result = await AccessModel.update({
  40. title,
  41. name,
  42. path,
  43. icon
  44. }, {
  45. where: {
  46. id
  47. }
  48. })
  49. return result
  50. }
  51. // 批量更新菜单
  52. export const updateBulkAccess = async (data: AccessModelProps[]) => {
  53. console.log('data', data)
  54. const result = await AccessModel.bulkCreate(data, {
  55. updateOnDuplicate: ['sort_id']
  56. })
  57. return result
  58. }

2-4 菜单model

src/db/models/access.ts

  1. import { DataTypes, Model, Optional } from 'sequelize'
  2. import seq from '../seq'
  3. export interface AccessModelProps {
  4. id: number;
  5. type: number;
  6. title: string;
  7. path: string;
  8. icon: string;
  9. name: string;
  10. sort_id: number;
  11. parent_id: number | null;
  12. status: 0 | 1;
  13. description: string;
  14. }
  15. interface AccessCreationAttributes extends Optional<AccessModelProps, "id"> {}
  16. interface AccessInstance
  17. extends Model<AccessModelProps, AccessCreationAttributes>,
  18. AccessModelProps {}
  19. const AccessModel = seq.define<AccessInstance>('Access', {
  20. id: {
  21. primaryKey: true,
  22. type: DataTypes.INTEGER,
  23. autoIncrement: true
  24. },
  25. type: {
  26. type: DataTypes.INTEGER,
  27. defaultValue: 1,
  28. comment: '权限类型:菜单'
  29. },
  30. title: {
  31. type: DataTypes.STRING,
  32. allowNull: false,
  33. comment: '标题名称'
  34. },
  35. path: {
  36. type: DataTypes.STRING,
  37. comment: 'url地址'
  38. },
  39. icon: {
  40. type: DataTypes.STRING,
  41. comment: 'icon名称'
  42. },
  43. name: {
  44. type: DataTypes.STRING,
  45. comment: '路由name'
  46. },
  47. sort_id: {
  48. type: DataTypes.INTEGER,
  49. allowNull: false,
  50. comment: '排序权重'
  51. },
  52. parent_id: {
  53. type: DataTypes.INTEGER,
  54. comment: '父id'
  55. },
  56. status: {
  57. type: DataTypes.BOOLEAN,
  58. defaultValue: 1,
  59. comment: '状态 0禁止 1正常'
  60. },
  61. description: {
  62. type: DataTypes.TEXT,
  63. comment: '描述'
  64. }
  65. })
  66. export default AccessModel

导入models/index
src/db/models/index.ts

  1. import UserModel from './user'
  2. import AccessModel from './access'
  3. export {
  4. UserModel,
  5. AccessModel
  6. }

同步下model

  1. npm run db

目前最新参考源码

https://gitee.com/brolly/vue3-admin-server