helper.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { isArray, isEmpty, isString, pickBy, omit } from 'lodash-es'
  2. import { h } from 'vue'
  3. import { RouterLink } from 'vue-router'
  4. import type { MenuProps, MenuDividerOption, MenuOption, MenuGroupOption } from 'naive-ui'
  5. import type { RouteRecordRaw } from 'vue-router'
  6. type NoIndex<T> = {
  7. [K in keyof T as string extends K ? never : K]: T[K]
  8. }
  9. type ReplaceKeys<T, R extends Partial<Record<keyof T, unknown>>> = T extends unknown
  10. ? {
  11. [K in keyof T]: K extends keyof R ? R[K] : T[K]
  12. } & Omit<R, keyof T>
  13. : never
  14. type CustomRouteRecordRaw = ReplaceKeys<
  15. RouteRecordRaw,
  16. {
  17. component: string
  18. }
  19. >
  20. type RouteOption = Omit<CustomRouteRecordRaw, 'children'> & {
  21. type?: never
  22. }
  23. type MergeMenuOption = ReplaceKeys<
  24. MenuOption,
  25. {
  26. icon: string
  27. children?: MergeMenuMixedOptions[]
  28. }
  29. > &
  30. RouteOption
  31. type MergeMenuGroup = NoIndex<
  32. ReplaceKeys<
  33. MenuGroupOption,
  34. {
  35. children?: Array<MergeMenuOption | MergeMenuDivider>
  36. }
  37. >
  38. >
  39. type MergeMenuDivider = NoIndex<MenuDividerOption>
  40. export type MergeMenuMixedOptions = MergeMenuOption | MergeMenuGroup | MergeMenuDivider
  41. export function resolveMenu(options: MergeMenuMixedOptions[], parentDisabled = false) {
  42. const menuOption: MenuProps['options'] = []
  43. options.forEach((item) => {
  44. if (!item.type || item.type === 'group') {
  45. const { children, name, path, label, icon, key, disabled, extra, props, show, type } =
  46. item as MergeMenuOption & { label: string }
  47. const mergedDisabled = parentDisabled || disabled
  48. const createIcon = icon ? () => h('span', { class: `${icon} size-6` }) : null
  49. const menu = pickBy(
  50. {
  51. key: key || (name as string) || (path as string),
  52. icon: createIcon,
  53. label,
  54. disabled,
  55. extra,
  56. props,
  57. show,
  58. type,
  59. },
  60. (v) => v !== undefined,
  61. ) as NonNullable<MenuProps['options']>[number]
  62. if (isArray(children) && !isEmpty(children)) {
  63. menu.children = resolveMenu(children, mergedDisabled)
  64. } else {
  65. menu.label = mergedDisabled
  66. ? label
  67. : () => h(RouterLink, { to: { name } }, { default: () => label })
  68. }
  69. menuOption.push(menu)
  70. } else {
  71. menuOption.push(item)
  72. }
  73. })
  74. return menuOption
  75. }
  76. export function resolveRoute(options: MergeMenuMixedOptions[]) {
  77. const modules = import.meta.glob('@/views/**/*.vue')
  78. const routeOptions: RouteRecordRaw[] = []
  79. function flattenOptions(options: MergeMenuMixedOptions[]): MergeMenuMixedOptions[] {
  80. return options.flatMap((item) => {
  81. if (item.type === 'divider') {
  82. return []
  83. }
  84. if (item.type === 'group' && isArray(item.children) && !isEmpty(item.children)) {
  85. return flattenOptions(item.children)
  86. }
  87. return [item]
  88. })
  89. }
  90. flattenOptions(options).forEach((item) => {
  91. const { label, icon, meta, component, children, disabled, ...rest } =
  92. item as MergeMenuOption & {
  93. label: string
  94. }
  95. if (!disabled) {
  96. let compModule: (() => Promise<unknown>) | null = null
  97. if (!isEmpty(component) && isString(component)) {
  98. const extractName = component.replace(/^\/|\.vue$/g, '')
  99. const modulePath = `/src/views/${extractName}.vue`
  100. if (modules[modulePath]) {
  101. compModule = modules[modulePath]
  102. }
  103. }
  104. const route = omit(
  105. {
  106. ...rest,
  107. ...(compModule ? { component: compModule } : {}),
  108. meta: {
  109. ...meta,
  110. label: meta?.label || label,
  111. icon,
  112. },
  113. },
  114. ['type', 'label', 'icon', 'disabled', 'extra', 'props', 'show', 'key'],
  115. ) as RouteRecordRaw
  116. if (isArray(children) && !isEmpty(children)) {
  117. route.children = resolveRoute(children)
  118. }
  119. routeOptions.push(route)
  120. }
  121. })
  122. return routeOptions
  123. }