smart-admin-web 10.20.0 → 10.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/api/index.d.ts +19 -1
  2. package/dist/api/index.js +85 -66
  3. package/dist/api/index.js.map +1 -1
  4. package/dist/api/schema.d.ts +448 -1
  5. package/dist/assets/icons/ph-subset.json.js +1 -1
  6. package/dist/components/FormContainer/index.vue.js +87 -75
  7. package/dist/components/FormContainer/index.vue.js.map +1 -1
  8. package/dist/components/UserPicker/UserAvatar.vue.d.ts +8 -0
  9. package/dist/components/UserPicker/UserAvatar.vue.js +8 -0
  10. package/dist/components/UserPicker/UserAvatar.vue.js.map +1 -0
  11. package/dist/components/UserPicker/UserAvatar2.vue.js +38 -0
  12. package/dist/components/UserPicker/UserAvatar2.vue.js.map +1 -0
  13. package/dist/components/UserPicker/index.vue.js +1 -1
  14. package/dist/components/UserPicker/index2.vue.js +328 -186
  15. package/dist/components/UserPicker/index2.vue.js.map +1 -1
  16. package/dist/index.js +248 -246
  17. package/dist/layouts/NoticeBell.vue.js +2 -2
  18. package/dist/layouts/NoticeBell2.vue.js +18 -17
  19. package/dist/layouts/NoticeBell2.vue.js.map +1 -1
  20. package/dist/lib/runtime.js +1 -1
  21. package/dist/lib/watermark.d.ts +1 -1
  22. package/dist/locales/en-US.d.ts +62 -3
  23. package/dist/locales/en-US.js +66 -7
  24. package/dist/locales/en-US.js.map +1 -1
  25. package/dist/locales/kernelMenuTitles.js +2 -1
  26. package/dist/locales/kernelMenuTitles.js.map +1 -1
  27. package/dist/locales/zh-CN.d.ts +62 -3
  28. package/dist/locales/zh-CN.js +66 -7
  29. package/dist/locales/zh-CN.js.map +1 -1
  30. package/dist/router/viewRegistry.js +6 -6
  31. package/dist/router/viewRegistry.js.map +1 -1
  32. package/dist/style.css +1 -1
  33. package/dist/theme/naive-theme.js +72 -67
  34. package/dist/theme/naive-theme.js.map +1 -1
  35. package/dist/types/api.d.ts +91 -0
  36. package/dist/types/api.js +17 -15
  37. package/dist/types/api.js.map +1 -1
  38. package/dist/utils/shake.d.ts +1 -0
  39. package/dist/utils/shake.js +30 -0
  40. package/dist/utils/shake.js.map +1 -0
  41. package/dist/views/login/LoginForm.vue.js +2 -2
  42. package/dist/views/login/LoginForm2.vue.js +135 -135
  43. package/dist/views/login/LoginForm2.vue.js.map +1 -1
  44. package/dist/views/login/components/LoginDesk.vue.js +1 -1
  45. package/dist/views/login/components/LoginDesk2.vue.js +59 -47
  46. package/dist/views/login/components/LoginDesk2.vue.js.map +1 -1
  47. package/dist/views/personal/notice2.vue.js +1 -1
  48. package/dist/views/personal/notice2.vue.js.map +1 -1
  49. package/dist/views/personal/sessions.vue.js +2 -2
  50. package/dist/views/personal/sessions2.vue.js +41 -40
  51. package/dist/views/personal/sessions2.vue.js.map +1 -1
  52. package/dist/views/system/ai-usage/index.vue.js +3 -3
  53. package/dist/views/system/ai-usage/index2.vue.js +13 -12
  54. package/dist/views/system/ai-usage/index2.vue.js.map +1 -1
  55. package/dist/views/system/config/components/AdvancedSection.vue.js +3 -3
  56. package/dist/views/system/config/components/AdvancedSection2.vue.js +2 -1
  57. package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -1
  58. package/dist/views/system/dict/index.vue.js +2 -2
  59. package/dist/views/system/dict/index2.vue.js +12 -1
  60. package/dist/views/system/dict/index2.vue.js.map +1 -1
  61. package/dist/views/system/file/index2.vue.js +1 -0
  62. package/dist/views/system/file/index2.vue.js.map +1 -1
  63. package/dist/views/system/job/index.vue.js +3 -3
  64. package/dist/views/system/job/index2.vue.js +182 -181
  65. package/dist/views/system/job/index2.vue.js.map +1 -1
  66. package/dist/views/system/job-log/components/AttemptTable2.vue.js +8 -3
  67. package/dist/views/system/job-log/components/AttemptTable2.vue.js.map +1 -1
  68. package/dist/views/system/job-log/index.vue.js +1 -1
  69. package/dist/views/system/job-log/index2.vue.js +5 -5
  70. package/dist/views/system/job-log/index2.vue.js.map +1 -1
  71. package/dist/views/system/log/exception/index.vue.js +2 -2
  72. package/dist/views/system/log/exception/index2.vue.js +8 -5
  73. package/dist/views/system/log/exception/index2.vue.js.map +1 -1
  74. package/dist/views/system/log/login/index2.vue.js +21 -12
  75. package/dist/views/system/log/login/index2.vue.js.map +1 -1
  76. package/dist/views/system/log/op/index.vue.js +2 -2
  77. package/dist/views/system/log/op/index2.vue.js +50 -40
  78. package/dist/views/system/log/op/index2.vue.js.map +1 -1
  79. package/dist/views/system/menu/components/ButtonManager.vue.js +2 -2
  80. package/dist/views/system/menu/components/ButtonManager2.vue.js +45 -36
  81. package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
  82. package/dist/views/system/menu/index.vue.js +2 -2
  83. package/dist/views/system/menu/index2.vue.js +11 -11
  84. package/dist/views/system/menu/index2.vue.js.map +1 -1
  85. package/dist/views/system/module/index.vue.js +1 -1
  86. package/dist/views/system/module/index2.vue.js +171 -117
  87. package/dist/views/system/module/index2.vue.js.map +1 -1
  88. package/dist/views/system/notice/index.vue.js +3 -3
  89. package/dist/views/system/notice/index2.vue.js +1 -0
  90. package/dist/views/system/notice/index2.vue.js.map +1 -1
  91. package/dist/views/system/org/index.vue.js +2 -2
  92. package/dist/views/system/org/index2.vue.js +70 -67
  93. package/dist/views/system/org/index2.vue.js.map +1 -1
  94. package/dist/views/system/position/index.vue.js +2 -2
  95. package/dist/views/system/position/index2.vue.js +57 -48
  96. package/dist/views/system/position/index2.vue.js.map +1 -1
  97. package/dist/views/system/recycle/index.vue.js +1 -1
  98. package/dist/views/system/recycle/index2.vue.js +3 -1
  99. package/dist/views/system/recycle/index2.vue.js.map +1 -1
  100. package/dist/views/system/role/components/GrantMenuSheet.vue.d.ts +2 -0
  101. package/dist/views/system/role/components/GrantMenuSheet.vue.js +2 -2
  102. package/dist/views/system/role/components/GrantMenuSheet2.vue.js +54 -49
  103. package/dist/views/system/role/components/GrantMenuSheet2.vue.js.map +1 -1
  104. package/dist/views/system/role/components/GrantMenuTable.vue.js +1 -1
  105. package/dist/views/system/role/components/GrantMenuTable2.vue.js +159 -195
  106. package/dist/views/system/role/components/GrantMenuTable2.vue.js.map +1 -1
  107. package/dist/views/system/role/components/grantMenuGroups.d.ts +54 -0
  108. package/dist/views/system/role/components/grantMenuGroups.js +116 -76
  109. package/dist/views/system/role/components/grantMenuGroups.js.map +1 -1
  110. package/dist/views/system/role/index.vue.js +2 -2
  111. package/dist/views/system/role/index2.vue.js +152 -145
  112. package/dist/views/system/role/index2.vue.js.map +1 -1
  113. package/dist/views/system/session/index2.vue.js +39 -35
  114. package/dist/views/system/session/index2.vue.js.map +1 -1
  115. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.d.ts +10 -0
  116. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js +5 -0
  117. package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js.map +1 -0
  118. package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js +96 -0
  119. package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js.map +1 -0
  120. package/dist/views/system/user/components/UserGrantMenuSheet.vue.d.ts +13 -0
  121. package/dist/views/system/user/components/UserGrantMenuSheet.vue.js +8 -0
  122. package/dist/views/system/user/components/UserGrantMenuSheet.vue.js.map +1 -0
  123. package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js +216 -0
  124. package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js.map +1 -0
  125. package/dist/views/system/user/components/UserGrantMenuTable.vue.d.ts +13 -0
  126. package/dist/views/system/user/components/UserGrantMenuTable.vue.js +8 -0
  127. package/dist/views/system/user/components/UserGrantMenuTable.vue.js.map +1 -0
  128. package/dist/views/system/user/components/UserGrantMenuTable2.vue.js +186 -0
  129. package/dist/views/system/user/components/UserGrantMenuTable2.vue.js.map +1 -0
  130. package/dist/views/system/user/components/UserGrantNodeRow.vue.d.ts +31 -0
  131. package/dist/views/system/user/components/UserGrantNodeRow.vue.js +8 -0
  132. package/dist/views/system/user/components/UserGrantNodeRow.vue.js.map +1 -0
  133. package/dist/views/system/user/components/UserGrantNodeRow2.vue.js +171 -0
  134. package/dist/views/system/user/components/UserGrantNodeRow2.vue.js.map +1 -0
  135. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.d.ts +21 -0
  136. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js +5 -0
  137. package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js.map +1 -0
  138. package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js +178 -0
  139. package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js.map +1 -0
  140. package/dist/views/system/user/components/userGrantState.d.ts +64 -0
  141. package/dist/views/system/user/components/userGrantState.js +117 -0
  142. package/dist/views/system/user/components/userGrantState.js.map +1 -0
  143. package/dist/views/system/user/index.vue.js +2 -2
  144. package/dist/views/system/user/index2.vue.js +250 -207
  145. package/dist/views/system/user/index2.vue.js.map +1 -1
  146. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"ButtonManager2.vue.js","sources":["../../../../../src/views/system/menu/components/ButtonManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 某个菜单/目录下「权限按钮」的专属管理弹窗 —— 按钮不进主菜单树、在这里单独管理,\n// 树里只放目录/菜单,不被几十个按钮撑长。按钮就是 type=Button 的 SysMenu 行,\n// 增删改复用 menuApi.add/update/remove(parentId=当前菜单),没有专用端点。\nimport { computed, h, reactive, ref } from 'vue'\nimport { useMediaQuery } from '@vueuse/core'\nimport {\n NButton,\n NSpace,\n NForm,\n NFormItemGi,\n NGrid,\n NInput,\n NInputNumber,\n NSelect,\n NSwitch,\n NPopconfirm,\n NCheckbox,\n NEmpty,\n NTag,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport StatusSwitch from '#/components/StatusSwitch/index.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nimport { useAuthStore } from '#/stores/auth'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport {\n MenuType,\n type MenuInput,\n type MenuTreeNode,\n joinPermission,\n splitPermission,\n} from '#/types/menu'\nimport type { PermissionRouteItem } from '#/types/api'\n\n// tree/routes 由父页传入并保持响应:父页 CRUD 后 load() 刷新 tree,本弹窗的 buttons 计算属性随之更新,\n// 无需自己再发一次整树请求。\nconst props = defineProps<{\n tree: MenuTreeNode[]\n routes: PermissionRouteItem[]\n /** 当前所属应用的路由匹配前缀(sys/biz…);空=不按应用过滤,全量显示。 */\n appPrefix?: string\n}>()\nconst emit = defineEmits<{ (e: 'changed'): void }>()\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst auth = useAuthStore()\n\n// 客户端权限门,与主表 menu/index.vue 对齐(按钮就是 type=Button 的 SysMenu 行,复用菜单 CRUD 路由权限码);\n// 服务端 [RolePermission] 始终兜底,这里只为不给无权用户显示写入口。\nconst canAdd = computed(() => auth.hasPerm('POST:/api/v1/sys/menu/add'))\nconst canEdit = computed(() => auth.hasPerm('PUT:/api/v1/sys/menu/{id}'))\nconst canDelete = computed(() => auth.hasPerm('DELETE:/api/v1/sys/menu/{id}'))\n\n// 窄屏:表单标签放上方(底部抽屉里横排标签太挤);宽 / 中档沿用左标签\nconst narrowForm = useMediaQuery('(max-width: 640px)')\nconst formLayout = computed(() =>\n narrowForm.value\n ? { labelPlacement: 'top' as const, labelWidth: undefined }\n : { labelPlacement: 'left' as const, labelWidth: 80 },\n)\n\nconst show = ref(false)\nconst menuId = ref<number | null>(null)\nconst menuTitle = ref('')\n\n/** 在整棵树里按 id 找节点(父页传的 tree 含按钮 children)。 */\nfunction findNode(nodes: MenuTreeNode[], id: number): MenuTreeNode | null {\n for (const n of nodes) {\n if (n.id === id) return n\n const hit = findNode(n.children, id)\n if (hit) return hit\n }\n return null\n}\n\n/** 当前菜单下的按钮子节点(随父页 tree 响应更新)。 */\nconst buttons = computed<MenuTreeNode[]>(() => {\n if (menuId.value == null) return []\n const node = findNode(props.tree, menuId.value)\n return (node?.children ?? []).filter(c => c.type === MenuType.Button)\n})\n\n/** 已占用的权限码(多码按钮展开成单条),用于批量添加去重。 */\nconst usedCodes = computed(() => new Set(buttons.value.flatMap(b => splitPermission(b.permission))))\n\n// ── 路由按应用软过滤 ────────────────────────────────────────────\n// route.path 形如 /api/v1/sys/...;appPrefix 是该应用的路由段(sys/biz)。空前缀=不过滤。\n// 软过滤:默认只列本应用路由,一键「显示全部」放开(业务页正当需要 /api/v1/sys/file 上传、/sys/dict 等共享系统路由)。\nconst routeSeg = computed(() => (props.appPrefix ?? '').trim().replace(/^\\/+|\\/+$/g, ''))\nconst belongsToApp = (path: string) => {\n const seg = routeSeg.value\n if (!seg) return true\n return path.includes(`/${seg}/`) || path.endsWith(`/${seg}`)\n}\n/** 是否存在「非本应用」的路由 —— 决定要不要展示「显示全部」开关。 */\nconst hasOtherRoutes = computed(\n () => routeSeg.value !== '' && props.routes.some(r => !belongsToApp(r.path)),\n)\nconst showAllRoutes = ref(false)\n\n// 权限码下拉(多选):label 展示「METHOD /路径」便于辨认,value 就是要写进 permission 的码本身。\n// 无前缀→平铺全量;有前缀且不显示全部→仅本应用;显示全部→按「本应用/其他应用」分组。\nconst toOpt = (r: PermissionRouteItem) => ({ label: `${r.method} ${r.path}`, value: r.code })\nconst permissionOptions = computed(() => {\n const seg = routeSeg.value\n if (!seg) return props.routes.map(toOpt)\n const inApp = props.routes.filter(r => belongsToApp(r.path))\n if (!showAllRoutes.value) return inApp.map(toOpt)\n const other = props.routes.filter(r => !belongsToApp(r.path))\n return [\n { type: 'group', key: '__inApp', label: t('menu.routesInApp'), children: inApp.map(toOpt) },\n { type: 'group', key: '__other', label: t('menu.routesOther'), children: other.map(toOpt) },\n ]\n})\n\n/**\n * 「所属页面」下拉:全部非按钮节点(目录 + 页面),缩进体现层级。\n * 不排除目录 —— 无对应页面的权限码锚点(如 ping)就该挂目录。\n * 按钮是叶子、不可能成环,故无需像主页 parentOptions 那样排除自身子树。\n */\nconst parentOptions = computed(() => {\n const opts: { label: string; value: number }[] = []\n const walk = (nodes: MenuTreeNode[], depth: number) => {\n for (const n of nodes) {\n if (n.type === MenuType.Button) continue\n opts.push({\n label: `${' '.repeat(depth)}${translateMenuTitle(n.title, n.path || undefined)}`,\n value: n.id,\n })\n walk(n.children, depth + 1)\n }\n }\n walk(props.tree, 0)\n return opts\n})\n\nfunction open(menu: { id: number; title: string }) {\n menuId.value = menu.id\n menuTitle.value = translateMenuTitle(menu.title)\n show.value = true\n}\ndefineExpose({ open })\n\n// ── 单个新增/编辑 ────────────────────────────────────────────────\nconst editShow = ref(false)\nconst editingId = ref<number | null>(null)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n title: {\n required: true,\n whitespace: true,\n message: () => t('menu.titleRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst blank = (): MenuInput => ({\n parentId: menuId.value ?? 0,\n type: MenuType.Button,\n title: '',\n permission: '',\n sort: 0,\n enabled: true,\n moduleId: null,\n path: '',\n component: '',\n icon: '',\n visible: true,\n})\nconst form = reactive<MenuInput>(blank())\n// 权限码在表单里按数组编辑(一颗按钮可挂多条路由),保存时拼回 form.permission 的 ; 串。\nconst permissionCodes = ref<string[]>([])\n\n/** 行 → 全量入参:回填与 StatusSwitch 行内改状态共用(后端无独立启停端点,均走全量 update)。 */\nconst toInput = (r: MenuTreeNode): MenuInput => ({\n parentId: r.parentId,\n type: r.type,\n title: r.title,\n permission: r.permission,\n sort: r.sort,\n enabled: r.enabled,\n moduleId: null,\n path: r.path ?? '',\n component: r.component ?? '',\n icon: r.icon ?? '',\n visible: r.visible,\n})\n\nfunction openAdd() {\n editingId.value = null\n showAllRoutes.value = false // 新增默认只看本应用路由\n Object.assign(form, blank())\n permissionCodes.value = []\n editShow.value = true\n}\nfunction openEdit(r: MenuTreeNode) {\n editingId.value = r.id\n showAllRoutes.value = true // 编辑时展开全部,保证已选的(可能跨应用的)权限码在下拉里可见\n Object.assign(form, toInput(r))\n permissionCodes.value = splitPermission(r.permission)\n editShow.value = true\n}\nasync function save() {\n await formRef.value?.validate()\n try {\n // parentId 取表单值(而非当前弹窗的 menuId)——「所属页面」改了就是移动:\n // 保存后本列表少一条、目标页面徽标 +1,由父页重拉树自然反映。\n const payload: MenuInput = {\n ...form,\n permission: joinPermission(permissionCodes.value),\n type: MenuType.Button,\n moduleId: null,\n }\n if (editingId.value === null) await menuApi.add(payload)\n else await menuApi.update(editingId.value, payload)\n message.success(t('menu.saved'))\n emit('changed')\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n\n// ── 从路由批量添加 ──────────────────────────────────────────────\ntype BatchRow = { code: string; method: string; path: string; title: string; checked: boolean }\nconst batchShow = ref(false)\nconst batchRows = ref<BatchRow[]>([])\nconst batchSaving = ref(false)\nconst batchKeyword = ref('')\n\n/** 搜索过滤:返回 batchRows 里命中的同一批对象引用,勾选/改标题的状态跨搜索保留;\n * saveBatch 仍以全量 batchRows 的 checked 为准,不会漏掉被过滤隐藏的已勾选行。 */\nconst filteredBatchRows = computed(() => {\n const kw = batchKeyword.value.trim().toLowerCase()\n const seg = routeSeg.value\n return batchRows.value.filter(r => {\n if (seg && !showAllRoutes.value && !belongsToApp(r.path)) return false // 按应用软过滤(可一键放开)\n if (!kw) return true\n return (\n r.method.toLowerCase().includes(kw) ||\n r.path.toLowerCase().includes(kw) ||\n r.title.toLowerCase().includes(kw)\n )\n })\n})\n\n/** HTTP 方法 → 默认标题(可就地改)。 */\nfunction defaultTitle(method: string): string {\n const m = method.toUpperCase()\n if (m === 'GET') return t('menu.btnQuery')\n if (m === 'POST') return t('menu.btnCreate')\n if (m === 'PUT' || m === 'PATCH') return t('menu.btnUpdate')\n if (m === 'DELETE') return t('menu.btnDelete')\n return method\n}\nfunction openBatch() {\n batchKeyword.value = ''\n showAllRoutes.value = false // 批量默认只列本应用路由\n // 只列当前菜单还没建过的路由,避免重复。\n batchRows.value = props.routes\n .filter(r => !usedCodes.value.has(r.code))\n .map(r => ({\n code: r.code,\n method: r.method,\n path: r.path,\n title: defaultTitle(r.method),\n checked: false,\n }))\n batchShow.value = true\n}\nconst batchChecked = computed(() => batchRows.value.filter(r => r.checked))\nasync function saveBatch() {\n const picked = batchChecked.value\n if (!picked.length) return false // 未选任何路由,别关弹窗\n batchSaving.value = true\n try {\n // ponytail: 逐个 add,量级大到卡顿再考虑后端批量端点\n for (const r of picked) {\n await menuApi.add({\n parentId: menuId.value ?? 0,\n type: MenuType.Button,\n title: r.title.trim() || r.method,\n permission: r.code,\n sort: 0,\n enabled: true,\n moduleId: null,\n path: '',\n component: '',\n icon: '',\n visible: true,\n })\n }\n message.success(t('menu.saved'))\n emit('changed')\n batchShow.value = false\n } catch (e) {\n message.error(translateError(e))\n return false\n } finally {\n batchSaving.value = false\n }\n}\n\nconst columns: SmartTableColumn<MenuTreeNode>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n { title: () => t('menu.title'), key: 'title', align: 'left', search: {} },\n {\n title: () => t('menu.permission'),\n key: 'permission',\n // 多码按钮逐条成 tag,一眼看出「查询」挂了哪几条路由\n render: r => {\n const codes = splitPermission(r.permission)\n if (!codes.length) return h('span', { class: 'faint' }, '—')\n return h(NSpace, { size: 4, wrapItem: false }, () =>\n codes.map(c => h(NTag, { size: 'small', bordered: false, key: c }, () => c)),\n )\n },\n },\n {\n title: () => t('menu.sort'),\n key: 'sort',\n width: 70,\n render: r => h('span', { class: 'num muted' }, r.sort),\n },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 84,\n render: r =>\n h(StatusSwitch, {\n value: r.enabled,\n request: (next: boolean) => menuApi.update(r.id, { ...toInput(r), enabled: next }),\n 'onUpdate:value': () => emit('changed'),\n }),\n },\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 130,\n align: 'right',\n fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, justify: 'end', wrapItem: false }, () => [\n canEdit.value\n ? h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openEdit(r) },\n () => t('common.edit'),\n )\n : null,\n canDelete.value\n ? h(\n NPopconfirm,\n {\n onPositiveClick: () =>\n run(() => menuApi.remove(r.id), t('menu.deleted')).then(ok => {\n if (ok) emit('changed')\n }),\n },\n {\n trigger: () =>\n h(NButton, { size: 'small', quaternary: true, type: 'error' }, () =>\n t('common.delete'),\n ),\n default: () => t('menu.deleteConfirm', { title: r.title }),\n },\n )\n : null,\n ]),\n },\n]\n</script>\n\n<template>\n <!-- 列表弹窗:只有一个「关闭」底栏(不走 onConfirm 提交协议) -->\n <FormContainer\n v-model:show=\"show\"\n :title=\"t('menu.buttonManagerTitle', { title: menuTitle })\"\n :width=\"960\"\n >\n <!-- 业务按钮在表格工具栏右半(次按钮在前、主按钮在后),不单独占一行放在表格上方 -->\n <SmartTable\n :columns=\"columns\"\n :data=\"buttons\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n :pagination=\"false\"\n row-key=\"id\"\n card-on-narrow\n storage-key=\"sys-menu-buttons\"\n >\n <template #toolbar-right>\n <n-button v-if=\"canAdd\" secondary @click=\"openBatch\">\n <template #icon><AppIcon icon=\"ph:list-plus\" :size=\"16\" /></template>\n {{ t('menu.batchFromRoutes') }}\n </n-button>\n <n-button v-if=\"canAdd\" type=\"primary\" @click=\"openAdd\">\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('menu.addButton') }}\n </n-button>\n </template>\n </SmartTable>\n <template #footer>\n <n-space justify=\"end\">\n <n-button @click=\"show = false\">{{ t('common.close') }}</n-button>\n </n-space>\n </template>\n </FormContainer>\n\n <!-- 单个新增/编辑按钮 -->\n <FormContainer\n v-model:show=\"editShow\"\n :title=\"editingId === null ? t('menu.addButton') : t('common.edit')\"\n :width=\"600\"\n :on-confirm=\"save\"\n :confirm-text=\"t('common.save')\"\n >\n <n-form\n ref=\"formRef\"\n :model=\"form\"\n :rules=\"rules\"\n require-mark-placement=\"left\"\n v-bind=\"formLayout\"\n >\n <n-grid :cols=\"1\" :y-gap=\"4\">\n <n-form-item-gi :label=\"t('menu.title')\" path=\"title\">\n <n-input v-model:value=\"form.title\" :placeholder=\"t('menu.title')\" />\n </n-form-item-gi>\n <!-- 改这里 = 把按钮移到别的页面(按钮不在主树里,这是唯一能改归属的入口)。 -->\n <n-form-item-gi :label=\"t('menu.buttonParent')\">\n <n-select v-model:value=\"form.parentId\" :options=\"parentOptions\" filterable />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.permission')\">\n <n-space vertical :size=\"4\" style=\"width: 100%\">\n <n-select\n v-model:value=\"permissionCodes\"\n :options=\"permissionOptions\"\n multiple\n filterable\n tag\n clearable\n :placeholder=\"t('menu.permissionPlaceholder')\"\n />\n <span class=\"perm-hint\">{{ t('menu.permissionHint') }}</span>\n <n-checkbox v-if=\"hasOtherRoutes\" v-model:checked=\"showAllRoutes\" size=\"small\">\n {{ t('menu.showAllRoutes') }}\n </n-checkbox>\n </n-space>\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.sort')\">\n <n-input-number v-model:value=\"form.sort\" :min=\"0\" style=\"width: 100%\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('common.status')\">\n <n-switch v-model:value=\"form.enabled\" />\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n\n <!-- 从路由批量添加 -->\n <FormContainer\n v-model:show=\"batchShow\"\n :title=\"t('menu.batchTitle')\"\n :width=\"640\"\n :on-confirm=\"saveBatch\"\n :confirm-text=\"t('common.save')\"\n >\n <n-empty\n v-if=\"!batchRows.length\"\n :description=\"t('menu.noRoutesLeft')\"\n style=\"padding: 24px 0\"\n />\n <n-space v-else vertical :size=\"10\">\n <n-space align=\"center\" :size=\"12\">\n <n-input\n v-model:value=\"batchKeyword\"\n clearable\n :placeholder=\"t('menu.batchSearchPlaceholder')\"\n style=\"flex: 1\"\n >\n <template #prefix><AppIcon icon=\"ph:magnifying-glass\" :size=\"16\" /></template>\n </n-input>\n <n-checkbox v-if=\"hasOtherRoutes\" v-model:checked=\"showAllRoutes\">\n {{ t('menu.showAllRoutes') }}\n </n-checkbox>\n </n-space>\n <n-empty\n v-if=\"!filteredBatchRows.length\"\n :description=\"t('menu.noRoutesLeft')\"\n style=\"padding: 16px 0\"\n />\n <n-space v-else vertical :size=\"6\" style=\"max-height: 55vh; overflow: auto\">\n <div v-for=\"r in filteredBatchRows\" :key=\"r.code\" class=\"batch-row\">\n <n-checkbox v-model:checked=\"r.checked\" />\n <span class=\"batch-route mono muted\">{{ r.method }} {{ r.path }}</span>\n <n-input v-model:value=\"r.title\" :disabled=\"!r.checked\" class=\"batch-title\" />\n </div>\n </n-space>\n </n-space>\n </FormContainer>\n</template>\n\n<style scoped>\n.perm-hint {\n font-size: 13px;\n color: var(--text-3);\n line-height: 1.5;\n}\n\n/* 批量添加:复选框 + 「METHOD 路径」+ 标题输入;窄屏路由文字独占一行,标题输入落到下一行 */\n.batch-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px 10px;\n}\n.batch-route {\n flex: 1 1 210px;\n min-width: 0;\n font-size: 13px;\n word-break: break-all;\n}\n.batch-title {\n width: 160px;\n}\n@media (max-width: 640px) {\n .batch-title {\n width: calc(100% - 34px);\n margin-left: 34px;\n }\n}\n\n/* 辅助字样(表格单元格由库渲染,用 :deep;批量行是自己的元素,直接命中) */\n.mono,\n:deep(.mono) {\n font-family: var(--font-mono);\n font-size: 13.5px;\n letter-spacing: 0;\n}\n.muted,\n:deep(.muted) {\n color: var(--text-2);\n}\n:deep(.faint) {\n color: var(--text-3);\n}\n:deep(.num) {\n font-variant-numeric: tabular-nums;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","run","useConfirm","auth","useAuthStore","canAdd","computed","canEdit","canDelete","narrowForm","useMediaQuery","formLayout","show","ref","menuId","menuTitle","findNode","nodes","id","hit","buttons","node","c","MenuType","usedCodes","b","splitPermission","routeSeg","belongsToApp","path","seg","hasOtherRoutes","r","showAllRoutes","toOpt","permissionOptions","inApp","other","parentOptions","opts","walk","depth","n","translateMenuTitle","open","menu","__expose","editShow","editingId","formRef","rules","blank","form","reactive","permissionCodes","toInput","openAdd","openEdit","save","_a","payload","joinPermission","menuApi","e","translateError","batchShow","batchRows","batchSaving","batchKeyword","filteredBatchRows","kw","defaultTitle","method","m","openBatch","batchChecked","saveBatch","picked","columns","codes","h","NSpace","NTag","StatusSwitch","next","NButton","NPopconfirm","ok","_createVNode","FormContainer","$event","_unref","SmartTable","TABLE_TOOLBAR","_createBlock","AppIcon","_createTextVNode","_mergeProps","NGrid","NFormItemGi","NInput","_cache","NSelect","_createElementVNode","_hoisted_1","_toDisplayString","NCheckbox","NInputNumber","NSwitch","_createElementBlock","_Fragment","_renderList","_hoisted_2","NEmpty"],"mappings":"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"}
1
+ {"version":3,"file":"ButtonManager2.vue.js","sources":["../../../../../src/views/system/menu/components/ButtonManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 某个菜单/目录下「权限按钮」的专属管理弹窗 —— 按钮不进主菜单树、在这里单独管理,\n// 树里只放目录/菜单,不被几十个按钮撑长。按钮就是 type=Button 的 SysMenu 行,\n// 增删改复用 menuApi.add/update/remove(parentId=当前菜单),没有专用端点。\nimport { computed, h, reactive, ref } from 'vue'\nimport { useMediaQuery } from '@vueuse/core'\nimport {\n NButton,\n NSpace,\n NForm,\n NFormItemGi,\n NGrid,\n NInput,\n NInputNumber,\n NSelect,\n NSwitch,\n NPopconfirm,\n NCheckbox,\n NEmpty,\n NTag,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport StatusSwitch from '#/components/StatusSwitch/index.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nimport { useAuthStore } from '#/stores/auth'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport {\n MenuType,\n type MenuInput,\n type MenuTreeNode,\n joinPermission,\n splitPermission,\n} from '#/types/menu'\nimport type { PermissionRouteItem } from '#/types/api'\n\n// tree/routes 由父页传入并保持响应:父页 CRUD 后 load() 刷新 tree,本弹窗的 buttons 计算属性随之更新,\n// 无需自己再发一次整树请求。\nconst props = defineProps<{\n tree: MenuTreeNode[]\n routes: PermissionRouteItem[]\n /** 当前所属应用的路由匹配前缀(sys/biz…);空=不按应用过滤,全量显示。 */\n appPrefix?: string\n}>()\nconst emit = defineEmits<{ (e: 'changed'): void }>()\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst auth = useAuthStore()\n\n// 客户端权限门,与主表 menu/index.vue 对齐(按钮就是 type=Button 的 SysMenu 行,复用菜单 CRUD 路由权限码);\n// 服务端 [RolePermission] 始终兜底,这里只为不给无权用户显示写入口。\nconst canAdd = computed(() => auth.hasPerm('POST:/api/v1/sys/menu/add'))\nconst canEdit = computed(() => auth.hasPerm('PUT:/api/v1/sys/menu/{id}'))\nconst canDelete = computed(() => auth.hasPerm('DELETE:/api/v1/sys/menu/{id}'))\n\n// 窄屏:表单标签放上方(底部抽屉里横排标签太挤);宽 / 中档沿用左标签\nconst narrowForm = useMediaQuery('(max-width: 640px)')\nconst formLayout = computed(() =>\n narrowForm.value\n ? { labelPlacement: 'top' as const, labelWidth: undefined }\n : { labelPlacement: 'left' as const, labelWidth: 80 },\n)\n\nconst show = ref(false)\nconst menuId = ref<number | null>(null)\nconst menuTitle = ref('')\n\n/** 在整棵树里按 id 找节点(父页传的 tree 含按钮 children)。 */\nfunction findNode(nodes: MenuTreeNode[], id: number): MenuTreeNode | null {\n for (const n of nodes) {\n if (n.id === id) return n\n const hit = findNode(n.children, id)\n if (hit) return hit\n }\n return null\n}\n\n/** 当前菜单下的按钮子节点(随父页 tree 响应更新)。 */\nconst buttons = computed<MenuTreeNode[]>(() => {\n if (menuId.value == null) return []\n const node = findNode(props.tree, menuId.value)\n return (node?.children ?? []).filter(c => c.type === MenuType.Button)\n})\n\n/** 已占用的权限码(多码按钮展开成单条),用于批量添加去重。 */\nconst usedCodes = computed(() => new Set(buttons.value.flatMap(b => splitPermission(b.permission))))\n\n// ── 路由按应用软过滤 ────────────────────────────────────────────\n// route.path 形如 /api/v1/sys/...;appPrefix 是该应用的路由段(sys/biz)。空前缀=不过滤。\n// 软过滤:默认只列本应用路由,一键「显示全部」放开(业务页正当需要 /api/v1/sys/file 上传、/sys/dict 等共享系统路由)。\nconst routeSeg = computed(() => (props.appPrefix ?? '').trim().replace(/^\\/+|\\/+$/g, ''))\nconst belongsToApp = (path: string) => {\n const seg = routeSeg.value\n if (!seg) return true\n return path.includes(`/${seg}/`) || path.endsWith(`/${seg}`)\n}\n/** 是否存在「非本应用」的路由 —— 决定要不要展示「显示全部」开关。 */\nconst hasOtherRoutes = computed(\n () => routeSeg.value !== '' && props.routes.some(r => !belongsToApp(r.path)),\n)\nconst showAllRoutes = ref(false)\n\n// 权限码下拉(多选):label 展示「METHOD /路径」便于辨认,value 就是要写进 permission 的码本身。\n// 无前缀→平铺全量;有前缀且不显示全部→仅本应用;显示全部→按「本应用/其他应用」分组。\nconst toOpt = (r: PermissionRouteItem) => ({ label: `${r.method} ${r.path}`, value: r.code })\nconst permissionOptions = computed(() => {\n const seg = routeSeg.value\n if (!seg) return props.routes.map(toOpt)\n const inApp = props.routes.filter(r => belongsToApp(r.path))\n if (!showAllRoutes.value) return inApp.map(toOpt)\n const other = props.routes.filter(r => !belongsToApp(r.path))\n return [\n { type: 'group', key: '__inApp', label: t('menu.routesInApp'), children: inApp.map(toOpt) },\n { type: 'group', key: '__other', label: t('menu.routesOther'), children: other.map(toOpt) },\n ]\n})\n\n/**\n * 「所属页面」下拉:全部非按钮节点(目录 + 页面),缩进体现层级。\n * 不排除目录 —— 无对应页面的权限码锚点(如 ping)就该挂目录。\n * 按钮是叶子、不可能成环,故无需像主页 parentOptions 那样排除自身子树。\n */\nconst parentOptions = computed(() => {\n const opts: { label: string; value: number }[] = []\n const walk = (nodes: MenuTreeNode[], depth: number) => {\n for (const n of nodes) {\n if (n.type === MenuType.Button) continue\n opts.push({\n label: `${' '.repeat(depth)}${translateMenuTitle(n.title, n.path || undefined)}`,\n value: n.id,\n })\n walk(n.children, depth + 1)\n }\n }\n walk(props.tree, 0)\n return opts\n})\n\nfunction open(menu: { id: number; title: string }) {\n menuId.value = menu.id\n menuTitle.value = translateMenuTitle(menu.title)\n show.value = true\n}\ndefineExpose({ open })\n\n// ── 单个新增/编辑 ────────────────────────────────────────────────\nconst editShow = ref(false)\nconst editingId = ref<number | null>(null)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n title: {\n required: true,\n whitespace: true,\n message: () => t('menu.titleRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst blank = (): MenuInput => ({\n parentId: menuId.value ?? 0,\n type: MenuType.Button,\n title: '',\n permission: '',\n sort: 0,\n enabled: true,\n moduleId: null,\n path: '',\n component: '',\n icon: '',\n visible: true,\n})\nconst form = reactive<MenuInput>(blank())\n// 权限码在表单里按数组编辑(一颗按钮可挂多条路由),保存时拼回 form.permission 的 ; 串。\nconst permissionCodes = ref<string[]>([])\n\n/** 行 → 全量入参:回填与 StatusSwitch 行内改状态共用(后端无独立启停端点,均走全量 update)。 */\nconst toInput = (r: MenuTreeNode): MenuInput => ({\n parentId: r.parentId,\n type: r.type,\n title: r.title,\n permission: r.permission,\n sort: r.sort,\n enabled: r.enabled,\n moduleId: null,\n path: r.path ?? '',\n component: r.component ?? '',\n icon: r.icon ?? '',\n visible: r.visible,\n})\n\nfunction openAdd() {\n editingId.value = null\n showAllRoutes.value = false // 新增默认只看本应用路由\n Object.assign(form, blank())\n permissionCodes.value = []\n editShow.value = true\n}\nfunction openEdit(r: MenuTreeNode) {\n editingId.value = r.id\n showAllRoutes.value = true // 编辑时展开全部,保证已选的(可能跨应用的)权限码在下拉里可见\n Object.assign(form, toInput(r))\n permissionCodes.value = splitPermission(r.permission)\n editShow.value = true\n}\nasync function save() {\n await formRef.value?.validate()\n try {\n // parentId 取表单值(而非当前弹窗的 menuId)——「所属页面」改了就是移动:\n // 保存后本列表少一条、目标页面徽标 +1,由父页重拉树自然反映。\n const payload: MenuInput = {\n ...form,\n permission: joinPermission(permissionCodes.value),\n type: MenuType.Button,\n moduleId: null,\n }\n if (editingId.value === null) await menuApi.add(payload)\n else await menuApi.update(editingId.value, payload)\n message.success(t('menu.saved'))\n emit('changed')\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n\n// ── 从路由批量添加 ──────────────────────────────────────────────\ntype BatchRow = { code: string; method: string; path: string; title: string; checked: boolean }\nconst batchShow = ref(false)\nconst batchRows = ref<BatchRow[]>([])\nconst batchSaving = ref(false)\nconst batchKeyword = ref('')\n\n/** 搜索过滤:返回 batchRows 里命中的同一批对象引用,勾选/改标题的状态跨搜索保留;\n * saveBatch 仍以全量 batchRows 的 checked 为准,不会漏掉被过滤隐藏的已勾选行。 */\nconst filteredBatchRows = computed(() => {\n const kw = batchKeyword.value.trim().toLowerCase()\n const seg = routeSeg.value\n return batchRows.value.filter(r => {\n if (seg && !showAllRoutes.value && !belongsToApp(r.path)) return false // 按应用软过滤(可一键放开)\n if (!kw) return true\n return (\n r.method.toLowerCase().includes(kw) ||\n r.path.toLowerCase().includes(kw) ||\n r.title.toLowerCase().includes(kw)\n )\n })\n})\n\n/** HTTP 方法 → 默认标题(可就地改)。 */\nfunction defaultTitle(method: string): string {\n const m = method.toUpperCase()\n if (m === 'GET') return t('menu.btnQuery')\n if (m === 'POST') return t('menu.btnCreate')\n if (m === 'PUT' || m === 'PATCH') return t('menu.btnUpdate')\n if (m === 'DELETE') return t('menu.btnDelete')\n return method\n}\nfunction openBatch() {\n batchKeyword.value = ''\n showAllRoutes.value = false // 批量默认只列本应用路由\n // 只列当前菜单还没建过的路由,避免重复。\n batchRows.value = props.routes\n .filter(r => !usedCodes.value.has(r.code))\n .map(r => ({\n code: r.code,\n method: r.method,\n path: r.path,\n title: defaultTitle(r.method),\n checked: false,\n }))\n batchShow.value = true\n}\nconst batchChecked = computed(() => batchRows.value.filter(r => r.checked))\nasync function saveBatch() {\n const picked = batchChecked.value\n if (!picked.length) return false // 未选任何路由,别关弹窗\n batchSaving.value = true\n try {\n // ponytail: 逐个 add,量级大到卡顿再考虑后端批量端点\n for (const r of picked) {\n await menuApi.add({\n parentId: menuId.value ?? 0,\n type: MenuType.Button,\n title: r.title.trim() || r.method,\n permission: r.code,\n sort: 0,\n enabled: true,\n moduleId: null,\n path: '',\n component: '',\n icon: '',\n visible: true,\n })\n }\n message.success(t('menu.saved'))\n emit('changed')\n batchShow.value = false\n } catch (e) {\n message.error(translateError(e))\n return false\n } finally {\n batchSaving.value = false\n }\n}\n\nconst columns: SmartTableColumn<MenuTreeNode>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n {\n title: () => t('menu.title'),\n key: 'title',\n ellipsis: { tooltip: true },\n align: 'left',\n search: {},\n },\n {\n title: () => t('menu.permission'),\n key: 'permission',\n // 权限码是 `METHOD:/api/...` 的整条路由,单个 tag 就比默认列宽长,所以给足宽度,放不下的一律省略 + 悬浮提示\n width: 360,\n ellipsis: { tooltip: true },\n // 多码按钮逐条成 tag,一眼看出「查询」挂了哪几条路由\n render: r => {\n const codes = splitPermission(r.permission)\n if (!codes.length) return h('span', { class: 'faint' }, '—')\n return h(NSpace, { size: 4, wrapItem: false }, () =>\n codes.map(c => h(NTag, { size: 'small', bordered: false, key: c }, () => c)),\n )\n },\n },\n {\n title: () => t('menu.sort'),\n key: 'sort',\n width: 70,\n render: r => h('span', { class: 'num muted' }, r.sort),\n },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 84,\n render: r =>\n h(StatusSwitch, {\n value: r.enabled,\n request: (next: boolean) => menuApi.update(r.id, { ...toInput(r), enabled: next }),\n 'onUpdate:value': () => emit('changed'),\n }),\n },\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 130,\n align: 'right',\n fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, justify: 'end', wrapItem: false }, () => [\n canEdit.value\n ? h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openEdit(r) },\n () => t('common.edit'),\n )\n : null,\n canDelete.value\n ? h(\n NPopconfirm,\n {\n onPositiveClick: () =>\n run(() => menuApi.remove(r.id), t('menu.deleted')).then(ok => {\n if (ok) emit('changed')\n }),\n },\n {\n trigger: () =>\n h(NButton, { size: 'small', quaternary: true, type: 'error' }, () =>\n t('common.delete'),\n ),\n default: () => t('menu.deleteConfirm', { title: r.title }),\n },\n )\n : null,\n ]),\n },\n]\n</script>\n\n<template>\n <!-- 列表弹窗:只有一个「关闭」底栏(不走 onConfirm 提交协议) -->\n <FormContainer\n v-model:show=\"show\"\n :title=\"t('menu.buttonManagerTitle', { title: menuTitle })\"\n :width=\"960\"\n >\n <!-- 业务按钮在表格工具栏右半(次按钮在前、主按钮在后),不单独占一行放在表格上方 -->\n <SmartTable\n :columns=\"columns\"\n :data=\"buttons\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n :pagination=\"false\"\n row-key=\"id\"\n card-on-narrow\n storage-key=\"sys-menu-buttons\"\n >\n <template #toolbar-right>\n <n-button v-if=\"canAdd\" secondary @click=\"openBatch\">\n <template #icon><AppIcon icon=\"ph:list-plus\" :size=\"16\" /></template>\n {{ t('menu.batchFromRoutes') }}\n </n-button>\n <n-button v-if=\"canAdd\" type=\"primary\" @click=\"openAdd\">\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('menu.addButton') }}\n </n-button>\n </template>\n </SmartTable>\n <template #footer>\n <n-space justify=\"end\">\n <n-button @click=\"show = false\">{{ t('common.close') }}</n-button>\n </n-space>\n </template>\n </FormContainer>\n\n <!-- 单个新增/编辑按钮 -->\n <FormContainer\n v-model:show=\"editShow\"\n :title=\"editingId === null ? t('menu.addButton') : t('common.edit')\"\n :width=\"600\"\n :on-confirm=\"save\"\n :confirm-text=\"t('common.save')\"\n >\n <n-form\n ref=\"formRef\"\n :model=\"form\"\n :rules=\"rules\"\n require-mark-placement=\"left\"\n v-bind=\"formLayout\"\n >\n <n-grid :cols=\"1\" :y-gap=\"4\">\n <n-form-item-gi :label=\"t('menu.title')\" path=\"title\">\n <n-input v-model:value=\"form.title\" :placeholder=\"t('menu.title')\" />\n </n-form-item-gi>\n <!-- 改这里 = 把按钮移到别的页面(按钮不在主树里,这是唯一能改归属的入口)。 -->\n <n-form-item-gi :label=\"t('menu.buttonParent')\">\n <n-select v-model:value=\"form.parentId\" :options=\"parentOptions\" filterable />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.permission')\">\n <n-space vertical :size=\"4\" style=\"width: 100%\">\n <n-select\n v-model:value=\"permissionCodes\"\n :options=\"permissionOptions\"\n multiple\n filterable\n tag\n clearable\n :placeholder=\"t('menu.permissionPlaceholder')\"\n />\n <span class=\"perm-hint\">{{ t('menu.permissionHint') }}</span>\n <n-checkbox v-if=\"hasOtherRoutes\" v-model:checked=\"showAllRoutes\" size=\"small\">\n {{ t('menu.showAllRoutes') }}\n </n-checkbox>\n </n-space>\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.sort')\">\n <n-input-number v-model:value=\"form.sort\" :min=\"0\" style=\"width: 100%\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('common.status')\">\n <n-switch v-model:value=\"form.enabled\" />\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n\n <!-- 从路由批量添加 -->\n <FormContainer\n v-model:show=\"batchShow\"\n :title=\"t('menu.batchTitle')\"\n :width=\"640\"\n :on-confirm=\"saveBatch\"\n :confirm-text=\"t('common.save')\"\n >\n <n-empty\n v-if=\"!batchRows.length\"\n :description=\"t('menu.noRoutesLeft')\"\n style=\"padding: 24px 0\"\n />\n <n-space v-else vertical :size=\"10\">\n <n-space align=\"center\" :size=\"12\">\n <n-input\n v-model:value=\"batchKeyword\"\n clearable\n :placeholder=\"t('menu.batchSearchPlaceholder')\"\n style=\"flex: 1\"\n >\n <template #prefix><AppIcon icon=\"ph:magnifying-glass\" :size=\"16\" /></template>\n </n-input>\n <n-checkbox v-if=\"hasOtherRoutes\" v-model:checked=\"showAllRoutes\">\n {{ t('menu.showAllRoutes') }}\n </n-checkbox>\n </n-space>\n <n-empty\n v-if=\"!filteredBatchRows.length\"\n :description=\"t('menu.noRoutesLeft')\"\n style=\"padding: 16px 0\"\n />\n <n-space v-else vertical :size=\"6\" style=\"max-height: 55vh; overflow: auto\">\n <div v-for=\"r in filteredBatchRows\" :key=\"r.code\" class=\"batch-row\">\n <n-checkbox v-model:checked=\"r.checked\" />\n <span class=\"batch-route mono muted\">{{ r.method }} {{ r.path }}</span>\n <n-input v-model:value=\"r.title\" :disabled=\"!r.checked\" class=\"batch-title\" />\n </div>\n </n-space>\n </n-space>\n </FormContainer>\n</template>\n\n<style scoped>\n.perm-hint {\n font-size: 13px;\n color: var(--text-3);\n line-height: 1.5;\n}\n\n/* 批量添加:复选框 + 「METHOD 路径」+ 标题输入;窄屏路由文字独占一行,标题输入落到下一行 */\n.batch-row {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px 10px;\n}\n.batch-route {\n flex: 1 1 210px;\n min-width: 0;\n font-size: 13px;\n word-break: break-all;\n}\n.batch-title {\n width: 160px;\n}\n@media (max-width: 640px) {\n .batch-title {\n width: calc(100% - 34px);\n margin-left: 34px;\n }\n}\n\n/* 辅助字样(表格单元格由库渲染,用 :deep;批量行是自己的元素,直接命中) */\n.mono,\n:deep(.mono) {\n font-family: var(--font-mono);\n font-size: 13.5px;\n letter-spacing: 0;\n}\n.muted,\n:deep(.muted) {\n color: var(--text-2);\n}\n:deep(.faint) {\n color: var(--text-3);\n}\n:deep(.num) {\n font-variant-numeric: tabular-nums;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","run","useConfirm","auth","useAuthStore","canAdd","computed","canEdit","canDelete","narrowForm","useMediaQuery","formLayout","show","ref","menuId","menuTitle","findNode","nodes","id","hit","buttons","node","c","MenuType","usedCodes","b","splitPermission","routeSeg","belongsToApp","path","seg","hasOtherRoutes","r","showAllRoutes","toOpt","permissionOptions","inApp","other","parentOptions","opts","walk","depth","n","translateMenuTitle","open","menu","__expose","editShow","editingId","formRef","rules","blank","form","reactive","permissionCodes","toInput","openAdd","openEdit","save","_a","payload","joinPermission","menuApi","e","translateError","batchShow","batchRows","batchSaving","batchKeyword","filteredBatchRows","kw","defaultTitle","method","m","openBatch","batchChecked","saveBatch","picked","columns","codes","h","NSpace","NTag","StatusSwitch","next","NButton","NPopconfirm","ok","_createVNode","FormContainer","$event","_unref","SmartTable","TABLE_TOOLBAR","_createBlock","AppIcon","_createTextVNode","_mergeProps","NGrid","NFormItemGi","NInput","_cache","NSelect","_createElementVNode","_hoisted_1","_toDisplayString","NCheckbox","NInputNumber","NSwitch","_createElementBlock","_Fragment","_renderList","_hoisted_2","NEmpty"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import o from "./index2.vue.js";
2
2
  /* empty css */
3
3
  import t from "../../../_virtual/_plugin-vue_export-helper.js";
4
- const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-4a7db6ca"]]);
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-f240ca18"]]);
5
5
  export {
6
- p as default
6
+ m as default
7
7
  };
8
8
  //# sourceMappingURL=index.vue.js.map
@@ -1,4 +1,4 @@
1
- import { defineComponent as pe, ref as p, shallowRef as A, onMounted as fe, computed as f, watch as ve, h as o, resolveDirective as he, openBlock as G, createElementBlock as ye, createVNode as v, unref as s, withCtx as h, createTextVNode as K, toDisplayString as V, withDirectives as ge, createBlock as we, toRaw as ke } from "vue";
1
+ import { defineComponent as pe, ref as p, shallowRef as A, onMounted as fe, computed as f, watch as ve, h as o, resolveDirective as he, openBlock as K, createElementBlock as ye, createVNode as v, unref as s, withCtx as h, createTextVNode as V, toDisplayString as W, withDirectives as ge, createBlock as we, toRaw as ke } from "vue";
2
2
  import { useMessage as be, NSpace as J, NTag as Q, NButton as w, NDropdown as _e, NSelect as xe } from "naive-ui";
3
3
  import { SmartTable as Te } from "smart-naive-table";
4
4
  import { useI18n as Ie } from "vue-i18n";
@@ -126,12 +126,12 @@ const He = { class: "view fill-page" }, b = 0, Y = -1, dt = /* @__PURE__ */ pe({
126
126
  ), me = f(() => {
127
127
  const e = /* @__PURE__ */ new Map(), t = (l, i) => {
128
128
  l.forEach((r, u) => {
129
- const j = i ? `${i}.${u + 1}` : String(u + 1);
130
- e.set(r.id, j), t(r.children, j);
129
+ const G = i ? `${i}.${u + 1}` : String(u + 1);
130
+ e.set(r.id, G), t(r.children, G);
131
131
  });
132
132
  };
133
133
  return t(M.value, ""), e;
134
- }), q = (e) => e ? o("span", { class: "mono muted" }, e) : o("span", { class: "faint" }, "—"), W = [
134
+ }), q = (e) => e ? o("span", { class: "mono muted" }, e) : o("span", { class: "faint" }, "—"), j = [
135
135
  // 序号列是渲染 rowNoById 层级号的普通数据列,fixed:'left' 与标题列同锚 ——
136
136
  // 不锚定的话,横向滚动时会滑到 fixed 的标题列底下,「这是哪一行」照样丢。
137
137
  {
@@ -161,7 +161,7 @@ const He = { class: "view fill-page" }, b = 0, Y = -1, dt = /* @__PURE__ */ pe({
161
161
  // 展开箭头与每层 26px 缩进钉死在这一列。默认会落到「首个无 type 的列」= 序号列,
162
162
  // 于是缩进把序号列里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。
163
163
  tree: !0,
164
- minWidth: 240,
164
+ width: 240,
165
165
  // 让出让给缩进的宽度
166
166
  fixed: "left",
167
167
  // 横向滚动时不能丢失「这是哪一行」
@@ -327,13 +327,13 @@ const He = { class: "view fill-page" }, b = 0, Y = -1, dt = /* @__PURE__ */ pe({
327
327
  })
328
328
  }
329
329
  ];
330
- return Re(W), (e, t) => {
330
+ return Re(j), (e, t) => {
331
331
  const l = he("auth");
332
- return G(), ye("div", He, [
332
+ return K(), ye("div", He, [
333
333
  v(s(Te), {
334
334
  ref_key: "tableRef",
335
335
  ref: F,
336
- columns: W,
336
+ columns: j,
337
337
  fetcher: ie,
338
338
  loading: T.value,
339
339
  "row-key": "id",
@@ -370,11 +370,11 @@ const He = { class: "view fill-page" }, b = 0, Y = -1, dt = /* @__PURE__ */ pe({
370
370
  }, null, 8, ["icon"])
371
371
  ]),
372
372
  default: h(() => [
373
- K(" " + V(z.value ? s(n)("common.collapseAll") : s(n)("common.expandAll")), 1)
373
+ V(" " + W(z.value ? s(n)("common.collapseAll") : s(n)("common.expandAll")), 1)
374
374
  ]),
375
375
  _: 1
376
376
  }),
377
- ge((G(), we(s(w), {
377
+ ge((K(), we(s(w), {
378
378
  type: "primary",
379
379
  onClick: t[1] || (t[1] = (i) => {
380
380
  var r;
@@ -388,7 +388,7 @@ const He = { class: "view fill-page" }, b = 0, Y = -1, dt = /* @__PURE__ */ pe({
388
388
  })
389
389
  ]),
390
390
  default: h(() => [
391
- K(" " + V(s(n)("common.add")), 1)
391
+ V(" " + W(s(n)("common.add")), 1)
392
392
  ]),
393
393
  _: 1
394
394
  })), [
@@ -1 +1 @@
1
- {"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/menu/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, h, onMounted, ref, shallowRef, toRaw, watch } from 'vue'\nimport {\n NButton,\n NSpace,\n NTag,\n NSelect,\n NDropdown,\n useMessage,\n type DropdownOption,\n} from 'naive-ui'\nimport {\n SmartTable,\n type SmartTableColumn,\n type SmartTableFetcher,\n type SmartTableInst,\n} from 'smart-naive-table'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport StatusSwitch from '#/components/StatusSwitch/index.vue'\nimport ButtonManager from './components/ButtonManager.vue'\nimport MenuFormModal from './components/MenuFormModal.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { buildRoutesForModule } from '#/composables/useAuthMenu'\nimport { useAuthStore } from '#/stores/auth'\nimport { menuApi, moduleApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { SEARCH_ACTIONS, deriveHeaderFilters } from '#/utils/tableFilter'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport { expandableIds } from '#/utils/tree'\nimport { filterTreeByParams, hasTreeCondition, type TreeSearchFields } from '#/utils/treeFilter'\nimport { MenuType, type MenuTreeNode, splitPermission, toMenuInput } from '#/types/menu'\nimport type { ModuleRow, PermissionRouteItem } from '#/types/api'\n\nimport { lineIcon } from '#/utils/menuIcon'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst auth = useAuthStore()\n\nconst loading = ref(false)\n// 整份替换 + 只读(行内改动一律重拉,不往节点上写值),不需要深响应\nconst tree = shallowRef<MenuTreeNode[]>([])\nconst modules = shallowRef<ModuleRow[]>([])\n/** 「未分配」哨兵:雪花 id 无 0,用它表示 moduleId==null 的顶级目录分组。 */\nconst UNASSIGNED = 0\n/** 「全部」哨兵:雪花 id 无负数,用它表示不按应用过滤。默认筛选停在当前应用时,\n * 别的应用底下新建的菜单会被筛掉、看着像\"存了但不见了\"——需要一个能看全量的退路。 */\nconst ALL_MODULES = -1\n/** 所属应用筛选:默认跟随当前进入的应用,modules 加载后再校正。 */\nconst moduleFilter = ref<number>(auth.currentModuleId ?? UNASSIGNED)\n/** 后端实时路由表(含消费方自建控制器)——权限码下拉的数据源,免手敲 `GET:/api/v1/...`。 */\nconst routes = ref<PermissionRouteItem[]>([])\n\n/** 从服务端拉整棵菜单树。只由 treeFetcher 调用:增删改后一律走 `reload()`(让表格重新取数),不要直接调它。 */\nasync function load() {\n loading.value = true\n try {\n tree.value = await menuApi.tree()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\n\n/**\n * 菜单改完顺手重建当前应用的壳层(侧边栏 + 动态路由)——否则新建的菜单要 F5 才出现,\n * 而这与「组件路径写错→进入 MissingRoute」都指向配置错误,这里仍需把失败显式反馈给管理员。\n * 失败静默:菜单本身已存成功,壳层没刷新不该把成功报成失败,下次 F5 自愈。\n */\nasync function syncShell() {\n if (!auth.currentModuleId) return\n try {\n await buildRoutesForModule(auth.currentModuleId)\n } catch {\n /* 见上 */\n }\n}\n// 菜单树本身由表格的 fetcher 首次取数时拉(immediate),这里只拉两份下拉辅助数据。\nonMounted(async () => {\n try {\n modules.value = await moduleApi.list()\n // 若默认筛选值不在选项内(如当前未进入任何应用),回退到第一个模块。\n if (!filterOptions.value.some(o => o.value === moduleFilter.value)) {\n moduleFilter.value = modules.value[0]?.id ?? UNASSIGNED\n }\n } catch {\n // 模块列表仅供「所属应用」下拉;拉取失败不阻塞菜单管理主流程。\n }\n try {\n routes.value = await menuApi.routes()\n } catch {\n // 路由清单仅供「权限码」下拉;拉取失败(如未授该码)退化为手输,不阻塞菜单管理主流程。\n }\n})\n\n// 类型文案。树里只有目录/页面(按钮不进树),Button 分支只为类型穷举。\nconst typeText = (ty: MenuType) =>\n ty === MenuType.Catalog\n ? t('menu.typeCatalog')\n : ty === MenuType.Menu\n ? t('menu.typeMenu')\n : t('menu.typeButton')\n\n// 目录/页面用不同色系(蓝/绿)区分 —— 树里只有这两类,按钮不进树。\nconst typeTagType = (ty: MenuType): 'info' | 'success' =>\n ty === MenuType.Catalog ? 'info' : 'success'\n\nconst moduleOptions = computed(() =>\n modules.value.map(m => ({ label: translateMenuTitle(m.title), value: m.id })),\n)\n\n/** 顶部筛选下拉:「全部」+ 各应用 +「未分配」。 */\nconst filterOptions = computed(() => [\n { label: t('menu.moduleAll'), value: ALL_MODULES },\n ...moduleOptions.value,\n { label: t('menu.moduleUnassigned'), value: UNASSIGNED },\n])\n\n/** 递归剔除按钮节点 —— 按钮统一在「权限按钮」弹窗里单独管理,不进主树,树里只留目录/菜单。 */\nfunction stripButtons(nodes: MenuTreeNode[]): MenuTreeNode[] {\n return nodes\n .filter(n => n.type !== MenuType.Button)\n .map(n => ({ ...n, children: stripButtons(n.children) }))\n}\n\n/**\n * 按所属应用过滤:moduleId 只存在顶级目录(parentId==0)上,子节点为 null 靠继承,\n * 所以只过滤顶级数组、子树自动跟随;UNASSIGNED 归拢 moduleId==null 的顶级目录。\n * 再剥掉按钮节点后交给表格渲染。\n */\nconst filteredTree = computed(() =>\n stripButtons(\n tree.value.filter(r =>\n moduleFilter.value === ALL_MODULES\n ? true\n : moduleFilter.value === UNASSIGNED\n ? r.moduleId == null\n : r.moduleId === moduleFilter.value,\n ),\n ),\n)\n\n/**\n * 各节点直属按钮的数量 + 权限码拼串(取自未剥离的原树,无需额外请求)。\n * count 给「权限按钮」列的徽标;perms 给关键字搜索 —— 按钮不在树里,\n * 「这个权限码归哪个页面」只能靠父页面命中来回答,否则藏起按钮就等于把它搜没了。\n */\nconst buttonInfoById = computed(() => {\n const m = new Map<number, { count: number; perms: string }>()\n const walk = (nodes: MenuTreeNode[]) => {\n for (const n of nodes) {\n const btns = n.children.filter(c => c.type === MenuType.Button)\n m.set(n.id, {\n count: btns.length,\n perms: btns\n .flatMap(b => splitPermission(b.permission))\n .join(' ')\n .toLowerCase(),\n })\n walk(n.children)\n }\n }\n walk(tree.value)\n return m\n})\n\n// ── 取数 + 条件搜索 + 展开控制 ────────────────────────────────────\n// 内核开箱就播了整棵系统菜单树,全展开、没搜索、没折叠 = 第一天打开就糊脸。\n// 树表用远程模式:3.0 静态模式的条件过滤是平铺的 rows.filter,不递归 children,在树上只会命中顶层行。\n// 所以 fetcher 自己递归过滤,条件构造器只负责收集条件(全局序列化器摊平成 { title, path, permission })。\n// 取数器每次都重新拉一遍服务端:工具栏内置的刷新图标只会重新调 fetcher,\n// 只过滤内存里的旧树的话,点刷新就是假刷新(别处新建的菜单永远出不来)。\n// 过滤结果只喂给表格,不喂给 parentOptions —— 父级下拉必须始终是完整的当前应用子树,不能随搜索缩水。\nconst tableRef = ref<SmartTableInst<MenuTreeNode>>()\nconst reload = () => tableRef.value?.refresh()\n\n// 搜索项 → 取值。权限码是特例:过滤跑在已剥掉按钮的 filteredTree 上,而目录/页面的 permission 恒为空,\n// 写成 n.permission 等于永不命中。要按权限码搜,只能查该节点「按钮子节点」的码(见 buttonInfoById)。\nconst SEARCH_FIELDS: TreeSearchFields<MenuTreeNode> = {\n // 英文界面下表里显示的是译名,搜索要两边都认(库里存的中文原文 + 当前语言的显示名)。\n title: n => `${n.title} ${translateMenuTitle(n.title, n.path || undefined)}`,\n path: n => n.path,\n permission: n => buttonInfoById.value.get(n.id)?.perms,\n}\n\n// 每次取数结果是否带搜索条件:挂在结果数组上,@loaded 时按数组引用取回。\n// 不用「最近一次 fetcher 调用的参数」是因为库会丢弃过期响应,@loaded 对应的未必是最后一次调用。\nconst searchedResults = new WeakMap<object, boolean>()\nconst treeFetcher: SmartTableFetcher<MenuTreeNode> = async params => {\n await load()\n const items = filterTreeByParams(filteredTree.value, params, SEARCH_FIELDS)\n searchedResults.set(items, hasTreeCondition(params, SEARCH_FIELDS))\n return { items, total: items.length }\n}\n\n// 展开受控:一旦传了 expanded-row-keys,naive 就以它为准(default-expand-all 会被初始值直接覆盖)。\n// 初值留空 = 进来全折叠:先给一屏顶级目录的骨架,要看哪一支自己点开;\n// 整棵树一进门就全摊开,反而得先滚半天才找得到自己要改的那条。\nconst expandedKeys = ref<number[]>([])\n// 取数器返回的(已过滤的)树:「展开全部」与层级序号都按它算。\nconst visibleTree = shallowRef<MenuTreeNode[]>([])\n// 当前展示的结果是不是搜索结果。\nlet searching = false\n\nfunction onLoaded(rows: MenuTreeNode[]) {\n visibleTree.value = rows\n const now = searchedResults.get(toRaw(rows)) ?? false\n if (now) {\n // 搜索是唯一必须自动展开的场景:filterTree 命中的节点可能埋在任意深度,祖先还折着就等于搜了个寂寞。\n // 结果每变一次重播一次(收窄条件也算)。\n expandedKeys.value = expandableIds(rows)\n } else if (searching) {\n // 条件清空 → 回到默认的全折叠。\n expandedKeys.value = []\n }\n // 无条件且上一次也没搜索:一律不碰展开态 ——\n // 保存菜单后会重拉树,无条件重置的话用户手点开的行会被一起按回去,改完一条就迷路一次。\n searching = now\n}\n\n// 切应用 = 换了一棵树,展开态归位(搜索中则交给 onLoaded 按新结果重新展开);同时让表格按新应用重新取数。\nwatch(moduleFilter, () => {\n if (!searching) expandedKeys.value = []\n void reload()\n})\n\nconst allExpanded = computed(() => expandedKeys.value.length > 0)\nconst toggleExpandAll = () =>\n (expandedKeys.value = allExpanded.value ? [] : expandableIds(visibleTree.value))\n\n// 当前所属应用的路由匹配前缀(sys/biz…):传给 ButtonManager,权限路由下拉据此按应用软过滤。\n// 「未分配」筛选或该应用未填 apiPrefix → undefined,ButtonManager 退化为不过滤。\nconst currentAppPrefix = computed(() =>\n moduleFilter.value === UNASSIGNED\n ? undefined\n : (modules.value.find(m => m.id === moduleFilter.value)?.apiPrefix ?? undefined),\n)\n\n// ── 新增/编辑弹窗(MenuFormModal:表单态/父级下拉/保存均在其内,父页只传数据 + 收 saved)──\nconst menuFormRef = ref<InstanceType<typeof MenuFormModal> | null>(null)\n\n// ── 权限按钮弹窗 ────────────────────────────────────────────────\nconst buttonMgrRef = ref<InstanceType<typeof ButtonManager> | null>(null)\nconst openButtons = (r: MenuTreeNode) => buttonMgrRef.value?.open({ id: r.id, title: r.title })\n/** 菜单/按钮增删改后:重拉树(刷新徽标)+ 重建壳层(即时反映授权/菜单变更)。表单弹窗与按钮弹窗共用。 */\nconst reloadTree = () => Promise.all([reload(), syncShell()])\n\n/** 删除:下拉菜单项不适合内联 popconfirm,走 useConfirm().confirm 的 dialog(它的设计用途)。 */\nconst onDelete = (r: MenuTreeNode) =>\n confirm({\n content: t('menu.deleteConfirm', { title: r.title }),\n type: 'error',\n action: () => menuApi.remove(r.id),\n successMsg: t('menu.deleted'),\n }).then(ok => {\n if (ok) reloadTree()\n })\n\n// 展开图标:naive 默认三角太淡、展开态朝下,辨识度低。统一成固定尺寸的「>」caret,展开时旋转 90°\n// —— 观感清晰,尺寸恒定,行高不会因默认图标忽大忽小而参差。\nconst renderExpandIcon = ({ expanded }: { expanded: boolean }) =>\n h(\n 'span',\n {\n style: `display:inline-flex;transition:transform .2s var(--ease);transform:rotate(${expanded ? 90 : 0}deg)`,\n },\n h(AppIcon, { icon: 'ph:caret-right', size: 14 }),\n )\n\n/**\n * 层级序号:`5` / `5.2` / `5.2.3`,按当前可见树逐层递推。\n * 不能用表格自带的 index 列 —— naive 树表 flatten 时让每个后代继承「根行下标」\n * (Body.tsx: traverse(r.children, rootIndex)),一整棵子树全叫「5」,序号等于没有。\n * 自己数则行行唯一,点号路径本身就是一条「到根」的定位。\n */\nconst rowNoById = computed(() => {\n const m = new Map<number, string>()\n const walk = (nodes: MenuTreeNode[], prefix: string) => {\n nodes.forEach((n, i) => {\n const no = prefix ? `${prefix}.${i + 1}` : String(i + 1)\n m.set(n.id, no)\n walk(n.children, no)\n })\n }\n walk(visibleTree.value, '')\n return m\n})\n\n/** 路由 / 组件路径单元格:有值用等宽次级色,空值画灰色的「—」。 */\nconst cellPath = (v?: string | null) =>\n v ? h('span', { class: 'mono muted' }, v) : h('span', { class: 'faint' }, '—')\n\nconst columns: SmartTableColumn<MenuTreeNode>[] = [\n // 序号列是渲染 rowNoById 层级号的普通数据列,fixed:'left' 与标题列同锚 ——\n // 不锚定的话,横向滚动时会滑到 fixed 的标题列底下,「这是哪一行」照样丢。\n {\n title: () => t('common.rowNo'),\n key: '__no',\n width: 72,\n align: 'center',\n fixed: 'left',\n hideInSetting: true, // 结构性列,不进列设置\n render: r => {\n const no = rowNoById.value.get(r.id)\n if (!no) return null\n const cut = no.lastIndexOf('.') + 1\n // 祖先前缀压暗、末段留亮:一眼读出「哪一层的第几个」,不用逐字符数点号。\n return h('span', { class: 'row-no' }, [\n cut ? h('span', { class: 'row-no__path' }, no.slice(0, cut)) : null,\n h('span', { class: 'row-no__self' }, no.slice(cut)),\n ])\n },\n },\n {\n title: () => t('menu.title'),\n key: 'title',\n search: { actions: SEARCH_ACTIONS.fuzzy },\n align: 'left', // 树列左对齐,展开缩进才好读\n // 展开箭头与每层 26px 缩进钉死在这一列。默认会落到「首个无 type 的列」= 序号列,\n // 于是缩进把序号列里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。\n tree: true,\n minWidth: 240, // 让出让给缩进的宽度\n fixed: 'left', // 横向滚动时不能丢失「这是哪一行」\n render: r =>\n // inline:必须行内 —— naive 的展开箭头是 inline-flex,默认块级 NSpace 会被挤到第二行。\n h(NSpace, { inline: true, align: 'center', size: 6, wrapItem: false }, () => [\n translateMenuTitle(r.title, r.path || undefined),\n // 「隐藏」是罕见状态(种子里的目录/页面全是显示),只在为真时才占视觉 —— 省掉一整列同一个词。\n r.visible\n ? null\n : h(NTag, { size: 'tiny', bordered: false, type: 'warning' }, () => t('common.hidden')),\n ]),\n },\n // 图标独占一列:留在标题列会和树形展开箭头 + 缩进挤在一起撑破首列换行。\n {\n title: () => t('menu.icon'),\n key: 'icon',\n width: 64,\n align: 'center',\n render: r => (r.icon ? h(AppIcon, { icon: lineIcon(r.icon), size: 18 }) : null),\n },\n // 类型独占一列,不塞在标题里。按类型着色,目录/页面一眼可辨。\n {\n title: () => t('menu.type'),\n key: 'type',\n width: 90,\n render: r =>\n h(NTag, { size: 'small', bordered: false, type: typeTagType(r.type) }, () =>\n typeText(r.type),\n ),\n },\n // 路由/组件路径:管理员核对菜单时真正要看的东西,直接列出,不必点开编辑弹窗。\n // 不设权限码列 —— 树里只有目录/页面,而它俩结构上永远没有权限码(种子里全为空),\n // 那一列必然是「—」。权限码的正主在「权限按钮」弹窗里。\n {\n title: () => t('menu.path'),\n key: 'path',\n align: 'left',\n width: 160,\n ellipsis: { tooltip: true },\n render: r => cellPath(r.path),\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n // 权限码只作搜索项:表格里不设这一列(见上),但「这个权限码归哪个页面」得能搜到 ——\n // 取值与匹配见 SEARCH_FIELDS.permission(查该节点按钮子节点的码)。\n {\n key: 'permission',\n title: () => t('menu.permission'),\n hideInTable: true,\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n {\n title: () => t('menu.component'),\n key: 'component',\n align: 'left',\n width: 180,\n ellipsis: { tooltip: true },\n render: r => cellPath(r.component),\n },\n {\n title: () => t('menu.buttons'),\n key: 'buttons',\n width: 140,\n render: r => {\n const n = buttonInfoById.value.get(r.id)?.count ?? 0\n // 页面和目录都能「配置权限」:按钮正常挂页面下;直接挂目录下的是无页面权限项\n //(只给移动端 / 第三方调的接口),目录就是它们的权限组,角色授权界面会把它们渲染成一行。\n // 无菜单新增权限则不显示「配置权限」入口(只读用户不该看到写入口;弹窗内写按钮亦各自设门,服务端兜底)\n if (!auth.hasPerm('POST:/api/v1/sys/menu/add')) return null\n return h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: n > 0 ? 'primary' : undefined,\n onClick: () => openButtons(r),\n },\n {\n icon: () => h(AppIcon, { icon: 'ph:shield-check', size: 15 }),\n default: () => t('menu.configPerms', { n }),\n },\n )\n },\n },\n {\n title: () => t('menu.sort'),\n key: 'sort',\n width: 70,\n render: r => h('span', { class: 'num muted' }, r.sort),\n },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 84,\n render: r =>\n h(StatusSwitch, {\n value: r.enabled,\n disabled: !auth.hasPerm('PUT:/api/v1/sys/menu/{id}'), // 启停走全量 update,无更新权限则置灰\n // 停用是一键隐藏整棵子树的重操作,先确认;启用无副作用,跳过确认(返回 null)。\n confirm: (next: boolean) => (next ? null : t('menu.disableConfirm', { title: r.title })),\n request: (next: boolean) => menuApi.update(r.id, { ...toMenuInput(r), enabled: next }),\n // 重拉而非往行对象上写:搜索态下的祖先行是 filterTree 的浅拷贝,写它不会回到源树(开关会弹回去)。\n // StatusSwitch 是悲观更新(请求成功才 emit),重拉一次就是最终态。\n 'onUpdate:value': () => {\n reload()\n syncShell() // 停用/启用即刻反映到侧边栏\n },\n }),\n },\n // 操作收敛成「编辑 + 更多▾」且 fixed:'right':4 个操作平铺会把「删除」挤到第二行、行高参差,\n // 不 fixed 的话横向滚动时整列会被裁掉够不着。\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 150,\n fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, wrapItem: false, justify: 'center' }, () => {\n // 「更多」里两项各自按权限码保留;都无权时整个下拉不渲染,避免空触发器。\n const canAddChild = auth.hasPerm('POST:/api/v1/sys/menu/add')\n const canDelete = auth.hasPerm('DELETE:/api/v1/sys/menu/{id}')\n const moreOptions: DropdownOption[] = [\n canAddChild && {\n key: 'addChild',\n label: t('menu.addChild'),\n icon: () => h(AppIcon, { icon: 'ph:plus', size: 16 }),\n },\n // 新增与删除之间一条分隔线;只剩一项时不画\n canAddChild && canDelete && { type: 'divider', key: 'divider' },\n canDelete && {\n key: 'delete',\n label: t('common.delete'),\n icon: () => h(AppIcon, { icon: 'ph:trash', size: 16 }),\n props: { style: 'color: var(--err)' },\n },\n ].filter(Boolean) as DropdownOption[]\n return [\n auth.hasPerm('PUT:/api/v1/sys/menu/{id}')\n ? h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: 'primary',\n onClick: () => menuFormRef.value?.openEdit(r),\n },\n () => t('common.edit'),\n )\n : null,\n moreOptions.length\n ? h(\n NDropdown,\n {\n trigger: 'click',\n options: moreOptions,\n onSelect: (key: string) =>\n key === 'addChild' ? menuFormRef.value?.openAdd(r.id) : onDelete(r),\n },\n () =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', iconPlacement: 'right' },\n {\n default: () => t('common.more'),\n icon: () => h(AppIcon, { icon: 'ph:caret-down', size: 12 }),\n },\n ),\n )\n : null,\n ]\n }),\n },\n]\nderiveHeaderFilters(columns)\n</script>\n\n<template>\n <!-- .fill-page 是站内约定(styles/layout.css):它的 :has() 规则把路由外层 .page-view 撑到 100%,\n 页面自身 flex 列 + overflow:hidden —— 卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"view fill-page\">\n <!-- expanded-row-keys / update:expanded-row-keys 不是 SmartTable 的 prop —— 它 inheritAttrs:false + v-bind=\"attrs\",\n 未声明的 attr 原样透传给内层 n-data-table(:loading 走的也是这条路)。 -->\n <SmartTable\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"treeFetcher\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n storage-key=\"sys-menu\"\n :indent=\"26\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n :render-expand-icon=\"renderExpandIcon\"\n :expanded-row-keys=\"expandedKeys\"\n @update:expanded-row-keys=\"(keys: number[]) => (expandedKeys = keys)\"\n @loaded=\"onLoaded\"\n >\n <!-- 应用选择器决定「看哪个应用的菜单树」,是限定数据范围的控件而不是业务按钮,所以留在工具栏左半。 -->\n <template #toolbar>\n <n-select\n v-model:value=\"moduleFilter\"\n :options=\"filterOptions\"\n style=\"width: 200px\"\n :placeholder=\"t('menu.module')\"\n />\n </template>\n <template #toolbar-right>\n <n-button secondary @click=\"toggleExpandAll\">\n <template #icon>\n <AppIcon\n :icon=\"allExpanded ? 'ph:arrows-in-line-vertical' : 'ph:arrows-out-line-vertical'\"\n :size=\"16\"\n />\n </template>\n {{ allExpanded ? t('common.collapseAll') : t('common.expandAll') }}\n </n-button>\n <n-button\n v-auth=\"'POST:/api/v1/sys/menu/add'\"\n type=\"primary\"\n @click=\"menuFormRef?.openAdd(0)\"\n >\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('common.add') }}\n </n-button>\n </template>\n </SmartTable>\n\n <MenuFormModal\n ref=\"menuFormRef\"\n :modules=\"modules\"\n :tree=\"tree\"\n :filtered-tree=\"filteredTree\"\n :module-filter=\"moduleFilter\"\n @saved=\"reloadTree\"\n />\n\n <ButtonManager\n ref=\"buttonMgrRef\"\n :tree=\"tree\"\n :routes=\"routes\"\n :app-prefix=\"currentAppPrefix\"\n @changed=\"reloadTree\"\n />\n </div>\n</template>\n\n<style scoped>\n.view {\n display: flex;\n flex-direction: column;\n gap: var(--gap-card);\n}\n\n/* 层级序号:字号/字族都跟标题列走(不用 12px 等宽),只借 tabular-nums 让各行点号竖着对齐;\n 前缀是祖先路径(压暗),末段才是这一行自己的号。 */\n:deep(.row-no) {\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n:deep(.row-no__path) {\n color: var(--text-3);\n}\n\n:deep(.row-no__self) {\n color: var(--text-2);\n font-weight: var(--font-weight-medium);\n}\n\n/* 单元格里的辅助字样:路径 / 组件用等宽,排序用等宽数字,空值灰色「—」(表格单元格由库渲染,要 :deep) */\n:deep(.mono) {\n font-family: var(--font-mono);\n font-size: 13.5px;\n letter-spacing: 0;\n}\n:deep(.muted) {\n color: var(--text-2);\n}\n:deep(.faint) {\n color: var(--text-3);\n}\n:deep(.num) {\n font-variant-numeric: tabular-nums;\n}\n</style>\n"],"names":["UNASSIGNED","ALL_MODULES","t","useI18n","message","useMessage","confirm","useConfirm","auth","useAuthStore","loading","ref","tree","shallowRef","modules","moduleFilter","routes","load","menuApi","translateError","syncShell","buildRoutesForModule","onMounted","moduleApi","filterOptions","o","_a","typeText","ty","MenuType","typeTagType","moduleOptions","computed","translateMenuTitle","m","stripButtons","nodes","n","filteredTree","r","buttonInfoById","walk","btns","c","b","splitPermission","tableRef","reload","SEARCH_FIELDS","searchedResults","treeFetcher","params","items","filterTreeByParams","hasTreeCondition","expandedKeys","visibleTree","searching","onLoaded","rows","now","toRaw","expandableIds","watch","allExpanded","toggleExpandAll","currentAppPrefix","menuFormRef","buttonMgrRef","openButtons","reloadTree","onDelete","ok","renderExpandIcon","expanded","h","AppIcon","rowNoById","prefix","i","no","cellPath","v","columns","cut","SEARCH_ACTIONS","NSpace","NTag","lineIcon","NButton","StatusSwitch","next","toMenuInput","canAddChild","canDelete","moreOptions","NDropdown","key","deriveHeaderFilters","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","TABLE_TOOLBAR","TABLE_MIN_ROW_HEIGHT","_cache","keys","NSelect","$event","_createTextVNode","_toDisplayString","_createBlock","MenuFormModal","ButtonManager"],"mappings":"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"}
1
+ {"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/menu/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, h, onMounted, ref, shallowRef, toRaw, watch } from 'vue'\nimport {\n NButton,\n NSpace,\n NTag,\n NSelect,\n NDropdown,\n useMessage,\n type DropdownOption,\n} from 'naive-ui'\nimport {\n SmartTable,\n type SmartTableColumn,\n type SmartTableFetcher,\n type SmartTableInst,\n} from 'smart-naive-table'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport StatusSwitch from '#/components/StatusSwitch/index.vue'\nimport ButtonManager from './components/ButtonManager.vue'\nimport MenuFormModal from './components/MenuFormModal.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { buildRoutesForModule } from '#/composables/useAuthMenu'\nimport { useAuthStore } from '#/stores/auth'\nimport { menuApi, moduleApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { SEARCH_ACTIONS, deriveHeaderFilters } from '#/utils/tableFilter'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport { expandableIds } from '#/utils/tree'\nimport { filterTreeByParams, hasTreeCondition, type TreeSearchFields } from '#/utils/treeFilter'\nimport { MenuType, type MenuTreeNode, splitPermission, toMenuInput } from '#/types/menu'\nimport type { ModuleRow, PermissionRouteItem } from '#/types/api'\n\nimport { lineIcon } from '#/utils/menuIcon'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst auth = useAuthStore()\n\nconst loading = ref(false)\n// 整份替换 + 只读(行内改动一律重拉,不往节点上写值),不需要深响应\nconst tree = shallowRef<MenuTreeNode[]>([])\nconst modules = shallowRef<ModuleRow[]>([])\n/** 「未分配」哨兵:雪花 id 无 0,用它表示 moduleId==null 的顶级目录分组。 */\nconst UNASSIGNED = 0\n/** 「全部」哨兵:雪花 id 无负数,用它表示不按应用过滤。默认筛选停在当前应用时,\n * 别的应用底下新建的菜单会被筛掉、看着像\"存了但不见了\"——需要一个能看全量的退路。 */\nconst ALL_MODULES = -1\n/** 所属应用筛选:默认跟随当前进入的应用,modules 加载后再校正。 */\nconst moduleFilter = ref<number>(auth.currentModuleId ?? UNASSIGNED)\n/** 后端实时路由表(含消费方自建控制器)——权限码下拉的数据源,免手敲 `GET:/api/v1/...`。 */\nconst routes = ref<PermissionRouteItem[]>([])\n\n/** 从服务端拉整棵菜单树。只由 treeFetcher 调用:增删改后一律走 `reload()`(让表格重新取数),不要直接调它。 */\nasync function load() {\n loading.value = true\n try {\n tree.value = await menuApi.tree()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\n\n/**\n * 菜单改完顺手重建当前应用的壳层(侧边栏 + 动态路由)——否则新建的菜单要 F5 才出现,\n * 而这与「组件路径写错→进入 MissingRoute」都指向配置错误,这里仍需把失败显式反馈给管理员。\n * 失败静默:菜单本身已存成功,壳层没刷新不该把成功报成失败,下次 F5 自愈。\n */\nasync function syncShell() {\n if (!auth.currentModuleId) return\n try {\n await buildRoutesForModule(auth.currentModuleId)\n } catch {\n /* 见上 */\n }\n}\n// 菜单树本身由表格的 fetcher 首次取数时拉(immediate),这里只拉两份下拉辅助数据。\nonMounted(async () => {\n try {\n modules.value = await moduleApi.list()\n // 若默认筛选值不在选项内(如当前未进入任何应用),回退到第一个模块。\n if (!filterOptions.value.some(o => o.value === moduleFilter.value)) {\n moduleFilter.value = modules.value[0]?.id ?? UNASSIGNED\n }\n } catch {\n // 模块列表仅供「所属应用」下拉;拉取失败不阻塞菜单管理主流程。\n }\n try {\n routes.value = await menuApi.routes()\n } catch {\n // 路由清单仅供「权限码」下拉;拉取失败(如未授该码)退化为手输,不阻塞菜单管理主流程。\n }\n})\n\n// 类型文案。树里只有目录/页面(按钮不进树),Button 分支只为类型穷举。\nconst typeText = (ty: MenuType) =>\n ty === MenuType.Catalog\n ? t('menu.typeCatalog')\n : ty === MenuType.Menu\n ? t('menu.typeMenu')\n : t('menu.typeButton')\n\n// 目录/页面用不同色系(蓝/绿)区分 —— 树里只有这两类,按钮不进树。\nconst typeTagType = (ty: MenuType): 'info' | 'success' =>\n ty === MenuType.Catalog ? 'info' : 'success'\n\nconst moduleOptions = computed(() =>\n modules.value.map(m => ({ label: translateMenuTitle(m.title), value: m.id })),\n)\n\n/** 顶部筛选下拉:「全部」+ 各应用 +「未分配」。 */\nconst filterOptions = computed(() => [\n { label: t('menu.moduleAll'), value: ALL_MODULES },\n ...moduleOptions.value,\n { label: t('menu.moduleUnassigned'), value: UNASSIGNED },\n])\n\n/** 递归剔除按钮节点 —— 按钮统一在「权限按钮」弹窗里单独管理,不进主树,树里只留目录/菜单。 */\nfunction stripButtons(nodes: MenuTreeNode[]): MenuTreeNode[] {\n return nodes\n .filter(n => n.type !== MenuType.Button)\n .map(n => ({ ...n, children: stripButtons(n.children) }))\n}\n\n/**\n * 按所属应用过滤:moduleId 只存在顶级目录(parentId==0)上,子节点为 null 靠继承,\n * 所以只过滤顶级数组、子树自动跟随;UNASSIGNED 归拢 moduleId==null 的顶级目录。\n * 再剥掉按钮节点后交给表格渲染。\n */\nconst filteredTree = computed(() =>\n stripButtons(\n tree.value.filter(r =>\n moduleFilter.value === ALL_MODULES\n ? true\n : moduleFilter.value === UNASSIGNED\n ? r.moduleId == null\n : r.moduleId === moduleFilter.value,\n ),\n ),\n)\n\n/**\n * 各节点直属按钮的数量 + 权限码拼串(取自未剥离的原树,无需额外请求)。\n * count 给「权限按钮」列的徽标;perms 给关键字搜索 —— 按钮不在树里,\n * 「这个权限码归哪个页面」只能靠父页面命中来回答,否则藏起按钮就等于把它搜没了。\n */\nconst buttonInfoById = computed(() => {\n const m = new Map<number, { count: number; perms: string }>()\n const walk = (nodes: MenuTreeNode[]) => {\n for (const n of nodes) {\n const btns = n.children.filter(c => c.type === MenuType.Button)\n m.set(n.id, {\n count: btns.length,\n perms: btns\n .flatMap(b => splitPermission(b.permission))\n .join(' ')\n .toLowerCase(),\n })\n walk(n.children)\n }\n }\n walk(tree.value)\n return m\n})\n\n// ── 取数 + 条件搜索 + 展开控制 ────────────────────────────────────\n// 内核开箱就播了整棵系统菜单树,全展开、没搜索、没折叠 = 第一天打开就糊脸。\n// 树表用远程模式:3.0 静态模式的条件过滤是平铺的 rows.filter,不递归 children,在树上只会命中顶层行。\n// 所以 fetcher 自己递归过滤,条件构造器只负责收集条件(全局序列化器摊平成 { title, path, permission })。\n// 取数器每次都重新拉一遍服务端:工具栏内置的刷新图标只会重新调 fetcher,\n// 只过滤内存里的旧树的话,点刷新就是假刷新(别处新建的菜单永远出不来)。\n// 过滤结果只喂给表格,不喂给 parentOptions —— 父级下拉必须始终是完整的当前应用子树,不能随搜索缩水。\nconst tableRef = ref<SmartTableInst<MenuTreeNode>>()\nconst reload = () => tableRef.value?.refresh()\n\n// 搜索项 → 取值。权限码是特例:过滤跑在已剥掉按钮的 filteredTree 上,而目录/页面的 permission 恒为空,\n// 写成 n.permission 等于永不命中。要按权限码搜,只能查该节点「按钮子节点」的码(见 buttonInfoById)。\nconst SEARCH_FIELDS: TreeSearchFields<MenuTreeNode> = {\n // 英文界面下表里显示的是译名,搜索要两边都认(库里存的中文原文 + 当前语言的显示名)。\n title: n => `${n.title} ${translateMenuTitle(n.title, n.path || undefined)}`,\n path: n => n.path,\n permission: n => buttonInfoById.value.get(n.id)?.perms,\n}\n\n// 每次取数结果是否带搜索条件:挂在结果数组上,@loaded 时按数组引用取回。\n// 不用「最近一次 fetcher 调用的参数」是因为库会丢弃过期响应,@loaded 对应的未必是最后一次调用。\nconst searchedResults = new WeakMap<object, boolean>()\nconst treeFetcher: SmartTableFetcher<MenuTreeNode> = async params => {\n await load()\n const items = filterTreeByParams(filteredTree.value, params, SEARCH_FIELDS)\n searchedResults.set(items, hasTreeCondition(params, SEARCH_FIELDS))\n return { items, total: items.length }\n}\n\n// 展开受控:一旦传了 expanded-row-keys,naive 就以它为准(default-expand-all 会被初始值直接覆盖)。\n// 初值留空 = 进来全折叠:先给一屏顶级目录的骨架,要看哪一支自己点开;\n// 整棵树一进门就全摊开,反而得先滚半天才找得到自己要改的那条。\nconst expandedKeys = ref<number[]>([])\n// 取数器返回的(已过滤的)树:「展开全部」与层级序号都按它算。\nconst visibleTree = shallowRef<MenuTreeNode[]>([])\n// 当前展示的结果是不是搜索结果。\nlet searching = false\n\nfunction onLoaded(rows: MenuTreeNode[]) {\n visibleTree.value = rows\n const now = searchedResults.get(toRaw(rows)) ?? false\n if (now) {\n // 搜索是唯一必须自动展开的场景:filterTree 命中的节点可能埋在任意深度,祖先还折着就等于搜了个寂寞。\n // 结果每变一次重播一次(收窄条件也算)。\n expandedKeys.value = expandableIds(rows)\n } else if (searching) {\n // 条件清空 → 回到默认的全折叠。\n expandedKeys.value = []\n }\n // 无条件且上一次也没搜索:一律不碰展开态 ——\n // 保存菜单后会重拉树,无条件重置的话用户手点开的行会被一起按回去,改完一条就迷路一次。\n searching = now\n}\n\n// 切应用 = 换了一棵树,展开态归位(搜索中则交给 onLoaded 按新结果重新展开);同时让表格按新应用重新取数。\nwatch(moduleFilter, () => {\n if (!searching) expandedKeys.value = []\n void reload()\n})\n\nconst allExpanded = computed(() => expandedKeys.value.length > 0)\nconst toggleExpandAll = () =>\n (expandedKeys.value = allExpanded.value ? [] : expandableIds(visibleTree.value))\n\n// 当前所属应用的路由匹配前缀(sys/biz…):传给 ButtonManager,权限路由下拉据此按应用软过滤。\n// 「未分配」筛选或该应用未填 apiPrefix → undefined,ButtonManager 退化为不过滤。\nconst currentAppPrefix = computed(() =>\n moduleFilter.value === UNASSIGNED\n ? undefined\n : (modules.value.find(m => m.id === moduleFilter.value)?.apiPrefix ?? undefined),\n)\n\n// ── 新增/编辑弹窗(MenuFormModal:表单态/父级下拉/保存均在其内,父页只传数据 + 收 saved)──\nconst menuFormRef = ref<InstanceType<typeof MenuFormModal> | null>(null)\n\n// ── 权限按钮弹窗 ────────────────────────────────────────────────\nconst buttonMgrRef = ref<InstanceType<typeof ButtonManager> | null>(null)\nconst openButtons = (r: MenuTreeNode) => buttonMgrRef.value?.open({ id: r.id, title: r.title })\n/** 菜单/按钮增删改后:重拉树(刷新徽标)+ 重建壳层(即时反映授权/菜单变更)。表单弹窗与按钮弹窗共用。 */\nconst reloadTree = () => Promise.all([reload(), syncShell()])\n\n/** 删除:下拉菜单项不适合内联 popconfirm,走 useConfirm().confirm 的 dialog(它的设计用途)。 */\nconst onDelete = (r: MenuTreeNode) =>\n confirm({\n content: t('menu.deleteConfirm', { title: r.title }),\n type: 'error',\n action: () => menuApi.remove(r.id),\n successMsg: t('menu.deleted'),\n }).then(ok => {\n if (ok) reloadTree()\n })\n\n// 展开图标:naive 默认三角太淡、展开态朝下,辨识度低。统一成固定尺寸的「>」caret,展开时旋转 90°\n// —— 观感清晰,尺寸恒定,行高不会因默认图标忽大忽小而参差。\nconst renderExpandIcon = ({ expanded }: { expanded: boolean }) =>\n h(\n 'span',\n {\n style: `display:inline-flex;transition:transform .2s var(--ease);transform:rotate(${expanded ? 90 : 0}deg)`,\n },\n h(AppIcon, { icon: 'ph:caret-right', size: 14 }),\n )\n\n/**\n * 层级序号:`5` / `5.2` / `5.2.3`,按当前可见树逐层递推。\n * 不能用表格自带的 index 列 —— naive 树表 flatten 时让每个后代继承「根行下标」\n * (Body.tsx: traverse(r.children, rootIndex)),一整棵子树全叫「5」,序号等于没有。\n * 自己数则行行唯一,点号路径本身就是一条「到根」的定位。\n */\nconst rowNoById = computed(() => {\n const m = new Map<number, string>()\n const walk = (nodes: MenuTreeNode[], prefix: string) => {\n nodes.forEach((n, i) => {\n const no = prefix ? `${prefix}.${i + 1}` : String(i + 1)\n m.set(n.id, no)\n walk(n.children, no)\n })\n }\n walk(visibleTree.value, '')\n return m\n})\n\n/** 路由 / 组件路径单元格:有值用等宽次级色,空值画灰色的「—」。 */\nconst cellPath = (v?: string | null) =>\n v ? h('span', { class: 'mono muted' }, v) : h('span', { class: 'faint' }, '—')\n\nconst columns: SmartTableColumn<MenuTreeNode>[] = [\n // 序号列是渲染 rowNoById 层级号的普通数据列,fixed:'left' 与标题列同锚 ——\n // 不锚定的话,横向滚动时会滑到 fixed 的标题列底下,「这是哪一行」照样丢。\n {\n title: () => t('common.rowNo'),\n key: '__no',\n width: 72,\n align: 'center',\n fixed: 'left',\n hideInSetting: true, // 结构性列,不进列设置\n render: r => {\n const no = rowNoById.value.get(r.id)\n if (!no) return null\n const cut = no.lastIndexOf('.') + 1\n // 祖先前缀压暗、末段留亮:一眼读出「哪一层的第几个」,不用逐字符数点号。\n return h('span', { class: 'row-no' }, [\n cut ? h('span', { class: 'row-no__path' }, no.slice(0, cut)) : null,\n h('span', { class: 'row-no__self' }, no.slice(cut)),\n ])\n },\n },\n {\n title: () => t('menu.title'),\n key: 'title',\n search: { actions: SEARCH_ACTIONS.fuzzy },\n align: 'left', // 树列左对齐,展开缩进才好读\n // 展开箭头与每层 26px 缩进钉死在这一列。默认会落到「首个无 type 的列」= 序号列,\n // 于是缩进把序号列里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。\n tree: true,\n width: 240, // 让出让给缩进的宽度\n fixed: 'left', // 横向滚动时不能丢失「这是哪一行」\n render: r =>\n // inline:必须行内 —— naive 的展开箭头是 inline-flex,默认块级 NSpace 会被挤到第二行。\n h(NSpace, { inline: true, align: 'center', size: 6, wrapItem: false }, () => [\n translateMenuTitle(r.title, r.path || undefined),\n // 「隐藏」是罕见状态(种子里的目录/页面全是显示),只在为真时才占视觉 —— 省掉一整列同一个词。\n r.visible\n ? null\n : h(NTag, { size: 'tiny', bordered: false, type: 'warning' }, () => t('common.hidden')),\n ]),\n },\n // 图标独占一列:留在标题列会和树形展开箭头 + 缩进挤在一起撑破首列换行。\n {\n title: () => t('menu.icon'),\n key: 'icon',\n width: 64,\n align: 'center',\n render: r => (r.icon ? h(AppIcon, { icon: lineIcon(r.icon), size: 18 }) : null),\n },\n // 类型独占一列,不塞在标题里。按类型着色,目录/页面一眼可辨。\n {\n title: () => t('menu.type'),\n key: 'type',\n width: 90,\n render: r =>\n h(NTag, { size: 'small', bordered: false, type: typeTagType(r.type) }, () =>\n typeText(r.type),\n ),\n },\n // 路由/组件路径:管理员核对菜单时真正要看的东西,直接列出,不必点开编辑弹窗。\n // 不设权限码列 —— 树里只有目录/页面,而它俩结构上永远没有权限码(种子里全为空),\n // 那一列必然是「—」。权限码的正主在「权限按钮」弹窗里。\n {\n title: () => t('menu.path'),\n key: 'path',\n align: 'left',\n width: 160,\n ellipsis: { tooltip: true },\n render: r => cellPath(r.path),\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n // 权限码只作搜索项:表格里不设这一列(见上),但「这个权限码归哪个页面」得能搜到 ——\n // 取值与匹配见 SEARCH_FIELDS.permission(查该节点按钮子节点的码)。\n {\n key: 'permission',\n title: () => t('menu.permission'),\n hideInTable: true,\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n {\n title: () => t('menu.component'),\n key: 'component',\n align: 'left',\n width: 180,\n ellipsis: { tooltip: true },\n render: r => cellPath(r.component),\n },\n {\n title: () => t('menu.buttons'),\n key: 'buttons',\n width: 140,\n render: r => {\n const n = buttonInfoById.value.get(r.id)?.count ?? 0\n // 页面和目录都能「配置权限」:按钮正常挂页面下;直接挂目录下的是无页面权限项\n //(只给移动端 / 第三方调的接口),目录就是它们的权限组,角色授权界面会把它们渲染成一行。\n // 无菜单新增权限则不显示「配置权限」入口(只读用户不该看到写入口;弹窗内写按钮亦各自设门,服务端兜底)\n if (!auth.hasPerm('POST:/api/v1/sys/menu/add')) return null\n return h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: n > 0 ? 'primary' : undefined,\n onClick: () => openButtons(r),\n },\n {\n icon: () => h(AppIcon, { icon: 'ph:shield-check', size: 15 }),\n default: () => t('menu.configPerms', { n }),\n },\n )\n },\n },\n {\n title: () => t('menu.sort'),\n key: 'sort',\n width: 70,\n render: r => h('span', { class: 'num muted' }, r.sort),\n },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 84,\n render: r =>\n h(StatusSwitch, {\n value: r.enabled,\n disabled: !auth.hasPerm('PUT:/api/v1/sys/menu/{id}'), // 启停走全量 update,无更新权限则置灰\n // 停用是一键隐藏整棵子树的重操作,先确认;启用无副作用,跳过确认(返回 null)。\n confirm: (next: boolean) => (next ? null : t('menu.disableConfirm', { title: r.title })),\n request: (next: boolean) => menuApi.update(r.id, { ...toMenuInput(r), enabled: next }),\n // 重拉而非往行对象上写:搜索态下的祖先行是 filterTree 的浅拷贝,写它不会回到源树(开关会弹回去)。\n // StatusSwitch 是悲观更新(请求成功才 emit),重拉一次就是最终态。\n 'onUpdate:value': () => {\n reload()\n syncShell() // 停用/启用即刻反映到侧边栏\n },\n }),\n },\n // 操作收敛成「编辑 + 更多▾」且 fixed:'right':4 个操作平铺会把「删除」挤到第二行、行高参差,\n // 不 fixed 的话横向滚动时整列会被裁掉够不着。\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 150,\n fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, wrapItem: false, justify: 'center' }, () => {\n // 「更多」里两项各自按权限码保留;都无权时整个下拉不渲染,避免空触发器。\n const canAddChild = auth.hasPerm('POST:/api/v1/sys/menu/add')\n const canDelete = auth.hasPerm('DELETE:/api/v1/sys/menu/{id}')\n const moreOptions: DropdownOption[] = [\n canAddChild && {\n key: 'addChild',\n label: t('menu.addChild'),\n icon: () => h(AppIcon, { icon: 'ph:plus', size: 16 }),\n },\n // 新增与删除之间一条分隔线;只剩一项时不画\n canAddChild && canDelete && { type: 'divider', key: 'divider' },\n canDelete && {\n key: 'delete',\n label: t('common.delete'),\n icon: () => h(AppIcon, { icon: 'ph:trash', size: 16 }),\n props: { style: 'color: var(--err)' },\n },\n ].filter(Boolean) as DropdownOption[]\n return [\n auth.hasPerm('PUT:/api/v1/sys/menu/{id}')\n ? h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: 'primary',\n onClick: () => menuFormRef.value?.openEdit(r),\n },\n () => t('common.edit'),\n )\n : null,\n moreOptions.length\n ? h(\n NDropdown,\n {\n trigger: 'click',\n options: moreOptions,\n onSelect: (key: string) =>\n key === 'addChild' ? menuFormRef.value?.openAdd(r.id) : onDelete(r),\n },\n () =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', iconPlacement: 'right' },\n {\n default: () => t('common.more'),\n icon: () => h(AppIcon, { icon: 'ph:caret-down', size: 12 }),\n },\n ),\n )\n : null,\n ]\n }),\n },\n]\nderiveHeaderFilters(columns)\n</script>\n\n<template>\n <!-- .fill-page 是站内约定(styles/layout.css):它的 :has() 规则把路由外层 .page-view 撑到 100%,\n 页面自身 flex 列 + overflow:hidden —— 卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"view fill-page\">\n <!-- expanded-row-keys / update:expanded-row-keys 不是 SmartTable 的 prop —— 它 inheritAttrs:false + v-bind=\"attrs\",\n 未声明的 attr 原样透传给内层 n-data-table(:loading 走的也是这条路)。 -->\n <SmartTable\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"treeFetcher\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n storage-key=\"sys-menu\"\n :indent=\"26\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n :render-expand-icon=\"renderExpandIcon\"\n :expanded-row-keys=\"expandedKeys\"\n @update:expanded-row-keys=\"(keys: number[]) => (expandedKeys = keys)\"\n @loaded=\"onLoaded\"\n >\n <!-- 应用选择器决定「看哪个应用的菜单树」,是限定数据范围的控件而不是业务按钮,所以留在工具栏左半。 -->\n <template #toolbar>\n <n-select\n v-model:value=\"moduleFilter\"\n :options=\"filterOptions\"\n style=\"width: 200px\"\n :placeholder=\"t('menu.module')\"\n />\n </template>\n <template #toolbar-right>\n <n-button secondary @click=\"toggleExpandAll\">\n <template #icon>\n <AppIcon\n :icon=\"allExpanded ? 'ph:arrows-in-line-vertical' : 'ph:arrows-out-line-vertical'\"\n :size=\"16\"\n />\n </template>\n {{ allExpanded ? t('common.collapseAll') : t('common.expandAll') }}\n </n-button>\n <n-button\n v-auth=\"'POST:/api/v1/sys/menu/add'\"\n type=\"primary\"\n @click=\"menuFormRef?.openAdd(0)\"\n >\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('common.add') }}\n </n-button>\n </template>\n </SmartTable>\n\n <MenuFormModal\n ref=\"menuFormRef\"\n :modules=\"modules\"\n :tree=\"tree\"\n :filtered-tree=\"filteredTree\"\n :module-filter=\"moduleFilter\"\n @saved=\"reloadTree\"\n />\n\n <ButtonManager\n ref=\"buttonMgrRef\"\n :tree=\"tree\"\n :routes=\"routes\"\n :app-prefix=\"currentAppPrefix\"\n @changed=\"reloadTree\"\n />\n </div>\n</template>\n\n<style scoped>\n.view {\n display: flex;\n flex-direction: column;\n gap: var(--gap-card);\n}\n\n/* 层级序号:字号/字族都跟标题列走(不用 12px 等宽),只借 tabular-nums 让各行点号竖着对齐;\n 前缀是祖先路径(压暗),末段才是这一行自己的号。 */\n:deep(.row-no) {\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n:deep(.row-no__path) {\n color: var(--text-3);\n}\n\n:deep(.row-no__self) {\n color: var(--text-2);\n font-weight: var(--font-weight-medium);\n}\n\n/* 单元格里的辅助字样:路径 / 组件用等宽,排序用等宽数字,空值灰色「—」(表格单元格由库渲染,要 :deep) */\n:deep(.mono) {\n font-family: var(--font-mono);\n font-size: 13.5px;\n letter-spacing: 0;\n}\n:deep(.muted) {\n color: var(--text-2);\n}\n:deep(.faint) {\n color: var(--text-3);\n}\n:deep(.num) {\n font-variant-numeric: tabular-nums;\n}\n</style>\n"],"names":["UNASSIGNED","ALL_MODULES","t","useI18n","message","useMessage","confirm","useConfirm","auth","useAuthStore","loading","ref","tree","shallowRef","modules","moduleFilter","routes","load","menuApi","translateError","syncShell","buildRoutesForModule","onMounted","moduleApi","filterOptions","o","_a","typeText","ty","MenuType","typeTagType","moduleOptions","computed","translateMenuTitle","m","stripButtons","nodes","n","filteredTree","r","buttonInfoById","walk","btns","c","b","splitPermission","tableRef","reload","SEARCH_FIELDS","searchedResults","treeFetcher","params","items","filterTreeByParams","hasTreeCondition","expandedKeys","visibleTree","searching","onLoaded","rows","now","toRaw","expandableIds","watch","allExpanded","toggleExpandAll","currentAppPrefix","menuFormRef","buttonMgrRef","openButtons","reloadTree","onDelete","ok","renderExpandIcon","expanded","h","AppIcon","rowNoById","prefix","i","no","cellPath","v","columns","cut","SEARCH_ACTIONS","NSpace","NTag","lineIcon","NButton","StatusSwitch","next","toMenuInput","canAddChild","canDelete","moreOptions","NDropdown","key","deriveHeaderFilters","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","TABLE_TOOLBAR","TABLE_MIN_ROW_HEIGHT","_cache","keys","NSelect","$event","_createTextVNode","_toDisplayString","_createBlock","MenuFormModal","ButtonManager"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import o from "./index2.vue.js";
2
2
  /* empty css */
3
3
  import t from "../../../_virtual/_plugin-vue_export-helper.js";
4
- const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-62ef6b1d"]]);
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-0ae40c45"]]);
5
5
  export {
6
6
  m as default
7
7
  };