smart-admin-web 10.5.0 → 10.10.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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/api/client.js.map +1 -1
  3. package/dist/api/index.d.ts +3 -3
  4. package/dist/api/index.js +2 -2
  5. package/dist/api/index.js.map +1 -1
  6. package/dist/components/DetailPage/index.vue.js +1 -1
  7. package/dist/components/DetailPage/index2.vue.js.map +1 -1
  8. package/dist/components/IconPicker/index.vue.js.map +1 -1
  9. package/dist/components/ImportWizard/index.vue.js +1 -1
  10. package/dist/components/ImportWizard/index2.vue.js.map +1 -1
  11. package/dist/components/TableZoomButton/index.vue.js.map +1 -1
  12. package/dist/composables/useConfirm.js.map +1 -1
  13. package/dist/composables/useLayoutMenu.js.map +1 -1
  14. package/dist/composables/useRealtime.d.ts +1 -1
  15. package/dist/composables/useRealtime.js.map +1 -1
  16. package/dist/createSmartAdmin.d.ts +2 -2
  17. package/dist/createSmartAdmin.js +15 -14
  18. package/dist/createSmartAdmin.js.map +1 -1
  19. package/dist/index.js +27 -26
  20. package/dist/layouts/default.vue.js +2 -2
  21. package/dist/layouts/default2.vue.js.map +1 -1
  22. package/dist/lib/icons.js.map +1 -1
  23. package/dist/lib/markdown.js.map +1 -1
  24. package/dist/lib/runtime.js +1 -1
  25. package/dist/locales/en-US.js +1 -1
  26. package/dist/locales/en-US.js.map +1 -1
  27. package/dist/locales/zh-CN.js +2 -2
  28. package/dist/locales/zh-CN.js.map +1 -1
  29. package/dist/router/detailRoutes.js.map +1 -1
  30. package/dist/router/index.js.map +1 -1
  31. package/dist/router/routes.js +1 -1
  32. package/dist/router/routes.js.map +1 -1
  33. package/dist/stores/user.d.ts +2 -2
  34. package/dist/stores/user.js.map +1 -1
  35. package/dist/style.css +1 -1
  36. package/dist/theme/mix.d.ts +3 -3
  37. package/dist/theme/mix.js.map +1 -1
  38. package/dist/types/api.d.ts +1 -1
  39. package/dist/types/api.js.map +1 -1
  40. package/dist/utils/url.d.ts +2 -0
  41. package/dist/utils/url.js +9 -3
  42. package/dist/utils/url.js.map +1 -1
  43. package/dist/views/error/404.vue.js +2 -2
  44. package/dist/views/error/4042.vue.js.map +1 -1
  45. package/dist/views/login/skins/SplitPanel.vue.js +1 -1
  46. package/dist/views/login/skins/SplitPanel2.vue.js.map +1 -1
  47. package/dist/views/system/log/op/index.vue.js +1 -1
  48. package/dist/views/system/log/op/index2.vue.js +1 -1
  49. package/dist/views/system/log/op/index2.vue.js.map +1 -1
  50. package/dist/views/system/menu/components/ButtonManager.vue.js +1 -1
  51. package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
  52. package/dist/views/system/menu/components/MenuFormModal.vue.js +3 -3
  53. package/dist/views/system/menu/components/MenuFormModal2.vue.js.map +1 -1
  54. package/dist/views/system/menu/index.vue.js +2 -2
  55. package/dist/views/system/menu/index2.vue.js +6 -6
  56. package/dist/views/system/menu/index2.vue.js.map +1 -1
  57. package/dist/views/system/module/index.vue.js +2 -2
  58. package/dist/views/system/module/index2.vue.js +2 -2
  59. package/dist/views/system/module/index2.vue.js.map +1 -1
  60. package/dist/views/system/user/components/ResetPasswordModal.vue.js +1 -1
  61. package/dist/views/system/user/components/ResetPasswordModal2.vue.js.map +1 -1
  62. package/dist/views/system/user/components/UserFormModal.vue.js +1 -1
  63. package/dist/views/system/user/components/UserFormModal2.vue.js.map +1 -1
  64. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SplitPanel2.vue.js","sources":["../../../../src/views/login/skins/SplitPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { useAppStore } from '#/stores/app'\nimport { useSite } from '#/composables/useSite'\nimport { mix, rgba } from '#/theme/mix'\nimport SmartLogo from '#/components/SmartLogo.vue'\nimport LoginForm from '../LoginForm.vue'\n\nconst { t } = useI18n()\nconst app = useAppStore()\nconst { site, appVersion } = useSite()\nconst year = new Date().getFullYear()\n\n// 卖点随语言切换(t 在 computed 内对 locale 响应)。\nconst points = computed(() => [t('login.featRbac'), t('login.featScope'), t('login.featPortal')])\n\nconst vars = computed(() => ({\n '--a1': rgba(app.accent, 0.5),\n '--a2': rgba(mix(app.accent, '#8B5CF6', 0.6), 0.4),\n}))\n</script>\n\n<template>\n <div class=\"split\" :style=\"vars\">\n <!-- 左:品牌面板(窄屏隐藏)-->\n <div class=\"panel\">\n <div class=\"panel-aurora\" />\n <div class=\"panel-inner\">\n <div class=\"logo\">\n <!-- 站点 logo 的取值顺序(配置 → brand 选项 → 内置矢量标)统一在 SmartLogo 里 -->\n <SmartLogo :size=\"34\" />\n <span>{{ site.title }}</span>\n </div>\n <div class=\"bar\" :style=\"{ background: app.accent }\" />\n <!-- 标题拆 pre/accent/post 三段以支持 i18n,中间词染 accent 色(整句硬编码中文切不了英文) -->\n <h1 class=\"headline\">\n {{ t('login.headlinePre') }}\n <span :style=\"{ color: app.accent }\">{{ t('login.headlineAccent') }}</span>\n {{ t('login.headlinePost') }}\n </h1>\n <p class=\"sub\">{{ site.subtitle || t('login.subtitle') }}</p>\n <ul class=\"points\">\n <li v-for=\"p in points\" :key=\"p\">\n <Icon icon=\"ph:check-circle-duotone\" :width=\"18\" :style=\"{ color: app.accent }\" />\n {{ p }}\n </li>\n </ul>\n </div>\n </div>\n\n <!-- 右:全高工作区(居中表单 + 版权页脚);主题/语言切换在外壳全局工具药丸 -->\n <div class=\"form-wrap\">\n <div class=\"mid\">\n <div class=\"form-inner\">\n <p class=\"greet\">{{ t('login.welcome') }}</p>\n <p class=\"greet-sub\">{{ t('login.welcomeSub') }}</p>\n <LoginForm :show-logo=\"false\" :show-title=\"false\" :show-footer=\"false\" />\n </div>\n </div>\n\n <p class=\"foot\">\n © {{ year }}\n <a v-if=\"site.copyrightUrl\" :href=\"site.copyrightUrl\" target=\"_blank\" rel=\"noopener\">\n {{ site.copyright || site.title }}\n </a>\n <template v-else>{{ site.copyright || site.title }}</template>\n <span v-if=\"appVersion\" class=\"foot-ver\">v{{ appVersion }}</span>\n </p>\n </div>\n </div>\n</template>\n\n<style scoped>\n.split {\n height: 100dvh;\n display: grid;\n grid-template-columns: 1.15fr 0.85fr;\n background: var(--color-bg-body);\n}\n/* 左栏 = recessed 品牌面(body 底 + 极光),与右栏 clean container 拉开图/底关系 */\n.panel {\n position: relative;\n overflow: hidden;\n background: var(--color-bg-body);\n border-right: 1px solid var(--color-border);\n}\n/* 细网格纹理,左上淡出 */\n.panel::before {\n content: '';\n position: absolute;\n inset: 0;\n background-image:\n linear-gradient(rgba(127, 127, 127, 0.08) 1px, transparent 1px),\n linear-gradient(90deg, rgba(127, 127, 127, 0.08) 1px, transparent 1px);\n background-size: 40px 40px;\n -webkit-mask-image: radial-gradient(circle at 30% 40%, #000, transparent 72%);\n mask-image: radial-gradient(circle at 30% 40%, #000, transparent 72%);\n}\n.panel-aurora {\n position: absolute;\n inset: -20%;\n background:\n radial-gradient(40% 40% at 25% 20%, var(--a1), transparent 68%),\n radial-gradient(45% 45% at 80% 78%, var(--a2), transparent 70%);\n filter: blur(45px);\n opacity: 0.9;\n animation: panel-move 22s ease-in-out infinite alternate;\n}\n@keyframes panel-move {\n 0% {\n transform: translate3d(-3%, -2%, 0) scale(1.05);\n }\n 100% {\n transform: translate3d(3%, 3%, 0) scale(1.12);\n }\n}\n.panel-inner {\n position: relative;\n z-index: 1;\n padding: 64px 56px;\n display: flex;\n flex-direction: column;\n height: 100%;\n justify-content: center;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 22px;\n font-weight: 700;\n color: #2a3c82;\n}\n/* 后台配置的品牌 logo:限高与内置矢量 logo(size=40)齐平,宽度自适应不变形 */\n.bar {\n width: 40px;\n height: 3px;\n border-radius: 2px;\n margin: 28px 0 20px;\n}\n.headline {\n font-size: 42px;\n line-height: 1.2;\n font-weight: 800;\n color: var(--color-text-primary);\n margin: 0;\n}\n.sub {\n color: var(--color-text-secondary);\n margin: 16px 0 28px;\n}\n.points {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.points li {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--color-text-secondary);\n}\n\n/* 右栏 = 全高工作区:居中表单 + 页脚(顶栏切换已上移到外壳全局工具) */\n.form-wrap {\n position: relative;\n display: flex;\n flex-direction: column;\n background: var(--color-bg-container);\n}\n.mid {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n.form-inner {\n width: 100%;\n max-width: 340px;\n animation: fade-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n.greet {\n font-size: 26px;\n font-weight: 700;\n letter-spacing: -0.01em;\n color: var(--color-text-primary);\n margin: 0 0 6px;\n}\n.greet-sub {\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n margin: 0 0 26px;\n}\n.foot {\n margin: 0;\n padding: 18px 24px 22px;\n text-align: center;\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n}\n.foot a {\n color: inherit;\n text-decoration: none;\n}\n.foot a:hover {\n color: var(--color-primary);\n}\n.foot-ver {\n margin-left: 8px;\n opacity: 0.75;\n}\n@keyframes fade-up {\n from {\n opacity: 0;\n transform: translateY(16px);\n }\n to {\n opacity: 1;\n transform: none;\n }\n}\n@media (max-width: 900px) {\n .split {\n grid-template-columns: 1fr;\n }\n .panel {\n display: none;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .panel-aurora,\n .form-inner {\n animation: none;\n }\n}\n</style>\n"],"names":["t","useI18n","app","useAppStore","site","appVersion","useSite","year","points","computed","vars","rgba","mix","_createElementBlock","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_createVNode","SmartLogo","_toDisplayString","_unref","_normalizeStyle","_hoisted_4","_createTextVNode","_hoisted_5","_hoisted_6","_Fragment","_renderList","p","Icon","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","LoginForm","_hoisted_12","_hoisted_13","_openBlock","_hoisted_14"],"mappings":"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"}
1
+ {"version":3,"file":"SplitPanel2.vue.js","sources":["../../../../src/views/login/skins/SplitPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useI18n } from 'vue-i18n'\nimport { useAppStore } from '#/stores/app'\nimport { useSite } from '#/composables/useSite'\nimport { mix, rgba } from '#/theme/mix'\nimport SmartLogo from '#/components/SmartLogo.vue'\nimport LoginForm from '../LoginForm.vue'\n\nconst { t } = useI18n()\nconst app = useAppStore()\nconst { site, appVersion } = useSite()\nconst year = new Date().getFullYear()\n\n// 卖点随语言切换(t 在 computed 内对 locale 响应)。\nconst points = computed(() => [t('login.featRbac'), t('login.featScope'), t('login.featPortal')])\n\nconst vars = computed(() => ({\n '--a1': rgba(app.accent, 0.5),\n '--a2': rgba(mix(app.accent, '#8B5CF6', 0.6), 0.4),\n}))\n</script>\n\n<template>\n <div class=\"split\" :style=\"vars\">\n <!-- 左:品牌面板(窄屏隐藏)-->\n <div class=\"panel\">\n <div class=\"panel-aurora\" />\n <div class=\"panel-inner\">\n <div class=\"logo\">\n <!-- 站点 logo 的取值顺序(配置 → brand 选项 → 内置矢量标)统一在 SmartLogo 里 -->\n <SmartLogo :size=\"34\" />\n <span>{{ site.title }}</span>\n </div>\n <div class=\"bar\" :style=\"{ background: app.accent }\" />\n <!-- 标题拆 pre/accent/post 三段以支持 i18n,中间词染 accent 色(整句硬编码中文切不了英文) -->\n <h1 class=\"headline\">\n {{ t('login.headlinePre') }}\n <span :style=\"{ color: app.accent }\">{{ t('login.headlineAccent') }}</span>\n {{ t('login.headlinePost') }}\n </h1>\n <p class=\"sub\">{{ site.subtitle || t('login.subtitle') }}</p>\n <ul class=\"points\">\n <li v-for=\"p in points\" :key=\"p\">\n <Icon icon=\"ph:check-circle-duotone\" :width=\"18\" :style=\"{ color: app.accent }\" />\n {{ p }}\n </li>\n </ul>\n </div>\n </div>\n\n <!-- 右:全高工作区(居中表单 + 版权页脚);主题/语言切换在外壳全局工具药丸 -->\n <div class=\"form-wrap\">\n <div class=\"mid\">\n <div class=\"form-inner\">\n <p class=\"greet\">{{ t('login.welcome') }}</p>\n <p class=\"greet-sub\">{{ t('login.welcomeSub') }}</p>\n <LoginForm :show-logo=\"false\" :show-title=\"false\" :show-footer=\"false\" />\n </div>\n </div>\n\n <p class=\"foot\">\n © {{ year }}\n <a v-if=\"site.copyrightUrl\" :href=\"site.copyrightUrl\" target=\"_blank\" rel=\"noopener\">\n {{ site.copyright || site.title }}\n </a>\n <template v-else>{{ site.copyright || site.title }}</template>\n <span v-if=\"appVersion\" class=\"foot-ver\">v{{ appVersion }}</span>\n </p>\n </div>\n </div>\n</template>\n\n<style scoped>\n.split {\n height: 100dvh;\n display: grid;\n grid-template-columns: 1.15fr 0.85fr;\n background: var(--color-bg-body);\n}\n/* 左栏 = recessed 品牌面(body 底 + 极光),与右栏 clean container 拉开图/底关系 */\n.panel {\n position: relative;\n overflow: hidden;\n background: var(--color-bg-body);\n border-right: 1px solid var(--color-border);\n}\n/* 细网格纹理,左上淡出 */\n.panel::before {\n content: '';\n position: absolute;\n inset: 0;\n background-image:\n linear-gradient(rgba(127, 127, 127, 0.08) 1px, transparent 1px),\n linear-gradient(90deg, rgba(127, 127, 127, 0.08) 1px, transparent 1px);\n background-size: 40px 40px;\n -webkit-mask-image: radial-gradient(circle at 30% 40%, #000, transparent 72%);\n mask-image: radial-gradient(circle at 30% 40%, #000, transparent 72%);\n}\n.panel-aurora {\n position: absolute;\n inset: -20%;\n background:\n radial-gradient(40% 40% at 25% 20%, var(--a1), transparent 68%),\n radial-gradient(45% 45% at 80% 78%, var(--a2), transparent 70%);\n filter: blur(45px);\n opacity: 0.9;\n animation: panel-move 22s ease-in-out infinite alternate;\n}\n@keyframes panel-move {\n 0% {\n transform: translate3d(-3%, -2%, 0) scale(1.05);\n }\n 100% {\n transform: translate3d(3%, 3%, 0) scale(1.12);\n }\n}\n.panel-inner {\n position: relative;\n z-index: 1;\n padding: 64px 56px;\n display: flex;\n flex-direction: column;\n height: 100%;\n justify-content: center;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 22px;\n font-weight: 700;\n color: #2a3c82;\n}\n/* 后台配置的品牌 logo:限高与内置矢量 logo(size=40)齐平,宽度自适应不变形 */\n.bar {\n width: 40px;\n height: 3px;\n border-radius: 2px;\n margin: 28px 0 20px;\n}\n.headline {\n font-size: 42px;\n line-height: 1.2;\n font-weight: 800;\n color: var(--color-text-primary);\n margin: 0;\n}\n.sub {\n color: var(--color-text-secondary);\n margin: 16px 0 28px;\n}\n.points {\n list-style: none;\n padding: 0;\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.points li {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--color-text-secondary);\n}\n\n/* 右栏 = 全高工作区:居中表单 + 页脚(主题/语言切换在外壳全局工具) */\n.form-wrap {\n position: relative;\n display: flex;\n flex-direction: column;\n background: var(--color-bg-container);\n}\n.mid {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n.form-inner {\n width: 100%;\n max-width: 340px;\n animation: fade-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n.greet {\n font-size: 26px;\n font-weight: 700;\n letter-spacing: -0.01em;\n color: var(--color-text-primary);\n margin: 0 0 6px;\n}\n.greet-sub {\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n margin: 0 0 26px;\n}\n.foot {\n margin: 0;\n padding: 18px 24px 22px;\n text-align: center;\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n}\n.foot a {\n color: inherit;\n text-decoration: none;\n}\n.foot a:hover {\n color: var(--color-primary);\n}\n.foot-ver {\n margin-left: 8px;\n opacity: 0.75;\n}\n@keyframes fade-up {\n from {\n opacity: 0;\n transform: translateY(16px);\n }\n to {\n opacity: 1;\n transform: none;\n }\n}\n@media (max-width: 900px) {\n .split {\n grid-template-columns: 1fr;\n }\n .panel {\n display: none;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .panel-aurora,\n .form-inner {\n animation: none;\n }\n}\n</style>\n"],"names":["t","useI18n","app","useAppStore","site","appVersion","useSite","year","points","computed","vars","rgba","mix","_createElementBlock","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_createVNode","SmartLogo","_toDisplayString","_unref","_normalizeStyle","_hoisted_4","_createTextVNode","_hoisted_5","_hoisted_6","_Fragment","_renderList","p","Icon","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","LoginForm","_hoisted_12","_hoisted_13","_openBlock","_hoisted_14"],"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-e1c2f8d3"]]);
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-9e9a338d"]]);
5
5
  export {
6
6
  m as default
7
7
  };
@@ -67,7 +67,7 @@ const Y = { class: "exception" }, Z = { key: 1 }, ge = /* @__PURE__ */ P({
67
67
  title: () => e("log.operator"),
68
68
  width: 120,
69
69
  render: (a) => M(a),
70
- // 日志只存 OperatorId(姓名是读取时回填的),故按人筛必须是精确 Id → 复用现成的人员选择器,搜索键改 operatorId。
70
+ // 日志只存 OperatorId(姓名是读取时回填的),故按人筛必须是精确 Id → 复用现成的人员选择器,搜索键用 operatorId。
71
71
  search: {
72
72
  key: "operatorId",
73
73
  label: () => e("log.operator"),
@@ -1 +1 @@
1
- {"version":3,"file":"index2.vue.js","sources":["../../../../../src/views/system/log/op/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 操作日志 = 只读 SmartTable + 详情抽屉。后端无 op/{id},分页项已含全字段 → 抽屉直接用行数据。\n// 搜索区要能答审计的三个问题:谁(操作人)、什么时候(时间范围)、干了什么(操作名/路径/成败)。\n// paramJson 走 CodeBlock(json 高亮 + 复制;美化仍是 parse→stringify,失败原样);异常堆栈保持危险色 <pre>——堆栈非代码,高亮无意义。\n// 导出:ExportColumnsModal 选列 + 当前筛选条件。\nimport { h, ref } from 'vue'\nimport {\n NButton,\n NTag,\n NDrawer,\n NDrawerContent,\n NDescriptions,\n NDescriptionsItem,\n useMessage,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn, type SmartTableInst } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport CodeBlock from '#/components/CodeBlock/index.vue'\nimport UserSelect from '#/components/UserSelect/index.vue'\nimport ExportColumnsModal from '#/components/ExportColumnsModal/index.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { logApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { operatorText } from '#/utils/format'\nimport { triggerBlobDownload } from '#/utils/download'\nimport type { ExportColumnDef, SysOpLog } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst tableRef = ref<SmartTableInst<SysOpLog>>()\n\nconst exportShow = ref(false)\nconst exporting = ref(false)\n\n/** 与后端 OpLogExportProfile.Columns 对齐。 */\nconst opExportColumns: ExportColumnDef[] = [\n { key: 'Title', title: t('log.opName') },\n { key: 'HttpMethod', title: t('log.method') },\n { key: 'Path', title: t('log.path') },\n { key: 'ResultCode', title: t('log.resultCode') },\n { key: 'Success', title: t('log.success') },\n { key: 'OperatorName', title: t('log.operator') },\n { key: 'Ip', title: t('log.ip') },\n { key: 'ElapsedMs', title: t('log.elapsed') },\n { key: 'CreateTime', title: t('common.createTime') },\n { key: 'ExceptionMessage', title: t('log.exceptionMessage'), defaultSelected: false },\n]\n\nasync function onExport(keys: string[]) {\n const p = tableRef.value?.params ?? {}\n exporting.value = true\n try {\n const blob = await logApi.opExport({\n title: p.title || undefined,\n success: p.success,\n operatorId: p.operatorId != null ? Number(p.operatorId) : undefined,\n path: p.path || undefined,\n createTime: p.createTime ?? null,\n columns: keys.join(','),\n })\n triggerBlobDownload(blob, t('logOp.fileName'))\n exportShow.value = false\n message.success(t('export.done'))\n } catch (e) {\n message.error(translateError(e))\n } finally {\n exporting.value = false\n }\n}\n\nconst columns: SmartTableColumn<SysOpLog>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n { key: 'title', title: () => t('log.opName'), search: true },\n { key: 'httpMethod', title: () => t('log.method'), width: 90 },\n { key: 'path', title: () => t('log.path'), ellipsis: { tooltip: true }, search: true },\n {\n key: 'success',\n title: () => t('log.result'),\n tag: true,\n search: true,\n options: [\n { label: () => t('log.success'), value: true, tagType: 'success' },\n { label: () => t('log.failed'), value: false, tagType: 'error' },\n ],\n },\n { key: 'resultCode', title: () => t('log.resultCode'), width: 100 },\n {\n key: 'operator',\n title: () => t('log.operator'),\n width: 120,\n render: r => operatorText(r),\n // 日志只存 OperatorId(姓名是读取时回填的),故按人筛必须是精确 Id → 复用现成的人员选择器,搜索键改 operatorId。\n search: {\n key: 'operatorId',\n label: () => t('log.operator'),\n render: ({ value, setValue, search }) =>\n h(UserSelect, {\n value: value as number | null,\n clearable: true,\n placeholder: t('log.operatorPlaceholder'),\n 'onUpdate:value': (v: number | null) => {\n setValue(v)\n search()\n },\n }),\n },\n },\n { key: 'elapsedMs', title: () => t('log.elapsed'), width: 100, render: r => `${r.elapsedMs} ms` },\n { key: 'ip', title: () => t('log.ip'), render: r => r.ip || '—' },\n // 时间范围是审计最常用的一刀(\"上周三下午谁删了那批数据\");daterange 回传 ['YYYY-MM-DD','YYYY-MM-DD'],\n // api 层拆成 StartTime/EndTime 并把结束日补到 23:59:59。\n {\n key: 'createTime',\n title: () => t('common.createTime'),\n format: 'datetime',\n search: { type: 'daterange' },\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 90,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openDetail(r) },\n () => t('log.detail'),\n ),\n },\n]\n\n// ── 详情抽屉(只读,行数据直填)──\nconst showDetail = ref(false)\nconst detailRow = ref<SysOpLog | null>(null)\nfunction openDetail(r: SysOpLog) {\n detailRow.value = r\n showDetail.value = true\n}\n/** paramJson 美化:parse→stringify(2);非法 JSON 原样返回。空值占位由模板判空。 */\nfunction prettyParam(json: string) {\n try {\n return JSON.stringify(JSON.parse(json), null, 2)\n } catch {\n return json\n }\n}\n\nfunction clearLogs() {\n confirm({\n type: 'error',\n content: t('log.clearOpConfirm'),\n action: () => logApi.opClear(),\n successMsg: t('log.cleared'),\n }).then(ok => {\n if (ok) tableRef.value?.refresh()\n })\n}\n</script>\n\n<template>\n <SmartTable\n :default-page-size=\"100\"\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"logApi.opPage\"\n flex-height\n virtual-scroll\n storage-key=\"sys-log-op\"\n @error=\"e => message.error(translateError(e))\"\n >\n <template #toolbar>\n <n-button v-auth=\"'DELETE:/api/v1/sys/log/op'\" type=\"error\" secondary @click=\"clearLogs\">\n <template #icon><AppIcon icon=\"ph:trash\" :size=\"16\" /></template>\n {{ t('log.clear') }}\n </n-button>\n <n-button v-auth=\"'GET:/api/v1/sys/log/op/export'\" @click=\"exportShow = true\">\n <template #icon><AppIcon icon=\"ph:download-simple\" :size=\"16\" /></template>\n {{ t('export.button') }}\n </n-button>\n </template>\n </SmartTable>\n\n <ExportColumnsModal\n v-model:show=\"exportShow\"\n :columns=\"opExportColumns\"\n :loading=\"exporting\"\n @confirm=\"onExport\"\n />\n\n <n-drawer v-model:show=\"showDetail\" :width=\"560\" placement=\"right\">\n <n-drawer-content :title=\"t('log.detail')\" closable>\n <n-descriptions v-if=\"detailRow\" label-placement=\"left\" :column=\"1\" bordered size=\"small\">\n <n-descriptions-item :label=\"t('log.opName')\">{{ detailRow.title }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.method')\">\n {{ detailRow.httpMethod }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.path')\">{{ detailRow.path }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.result')\">\n <n-tag :type=\"detailRow.success ? 'success' : 'error'\" size=\"small\" :bordered=\"false\">\n {{ detailRow.success ? t('log.success') : t('log.failed') }}\n </n-tag>\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.resultCode')\">\n {{ detailRow.resultCode }}\n </n-descriptions-item>\n <n-descriptions-item v-if=\"detailRow.exceptionMessage\" :label=\"t('log.exception')\">\n <pre class=\"exception\">{{ detailRow.exceptionMessage }}</pre>\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.elapsed')\">\n {{ detailRow.elapsedMs }} ms\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.operator')\">\n {{ operatorText(detailRow) }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.ip')\">{{ detailRow.ip || '—' }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.userAgent')\">\n {{ detailRow.userAgent || '—' }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('common.createTime')\">\n {{ detailRow.createTime }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.param')\">\n <CodeBlock v-if=\"detailRow.paramJson\" :code=\"prettyParam(detailRow.paramJson)\" />\n <span v-else>—</span>\n </n-descriptions-item>\n </n-descriptions>\n </n-drawer-content>\n </n-drawer>\n</template>\n\n<style scoped>\n.exception {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: 12px;\n line-height: 1.5;\n color: var(--color-danger);\n}\n</style>\n"],"names":["t","useI18n","message","useMessage","confirm","useConfirm","tableRef","ref","exportShow","exporting","opExportColumns","onExport","keys","p","_a","blob","logApi","triggerBlobDownload","e","translateError","columns","r","operatorText","value","setValue","search","h","UserSelect","v","NButton","openDetail","showDetail","detailRow","prettyParam","json","clearLogs","ok","_createVNode","_unref","SmartTable","_cache","_createBlock","AppIcon","_createTextVNode","ExportColumnsModal","$event","NDrawer","NDrawerContent","NDescriptions","NDescriptionsItem","_toDisplayString","NTag","_createElementVNode","_hoisted_1","CodeBlock","_openBlock","_createElementBlock"],"mappings":"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"}
1
+ {"version":3,"file":"index2.vue.js","sources":["../../../../../src/views/system/log/op/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 操作日志 = 只读 SmartTable + 详情抽屉。后端无 op/{id},分页项已含全字段 → 抽屉直接用行数据。\n// 搜索区要能答审计的三个问题:谁(操作人)、什么时候(时间范围)、干了什么(操作名/路径/成败)。\n// paramJson 走 CodeBlock(json 高亮 + 复制;美化是 parse→stringify,失败原样);异常堆栈保持危险色 <pre>——堆栈非代码,高亮无意义。\n// 导出:ExportColumnsModal 选列 + 当前筛选条件。\nimport { h, ref } from 'vue'\nimport {\n NButton,\n NTag,\n NDrawer,\n NDrawerContent,\n NDescriptions,\n NDescriptionsItem,\n useMessage,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn, type SmartTableInst } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport CodeBlock from '#/components/CodeBlock/index.vue'\nimport UserSelect from '#/components/UserSelect/index.vue'\nimport ExportColumnsModal from '#/components/ExportColumnsModal/index.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { logApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { operatorText } from '#/utils/format'\nimport { triggerBlobDownload } from '#/utils/download'\nimport type { ExportColumnDef, SysOpLog } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst tableRef = ref<SmartTableInst<SysOpLog>>()\n\nconst exportShow = ref(false)\nconst exporting = ref(false)\n\n/** 与后端 OpLogExportProfile.Columns 对齐。 */\nconst opExportColumns: ExportColumnDef[] = [\n { key: 'Title', title: t('log.opName') },\n { key: 'HttpMethod', title: t('log.method') },\n { key: 'Path', title: t('log.path') },\n { key: 'ResultCode', title: t('log.resultCode') },\n { key: 'Success', title: t('log.success') },\n { key: 'OperatorName', title: t('log.operator') },\n { key: 'Ip', title: t('log.ip') },\n { key: 'ElapsedMs', title: t('log.elapsed') },\n { key: 'CreateTime', title: t('common.createTime') },\n { key: 'ExceptionMessage', title: t('log.exceptionMessage'), defaultSelected: false },\n]\n\nasync function onExport(keys: string[]) {\n const p = tableRef.value?.params ?? {}\n exporting.value = true\n try {\n const blob = await logApi.opExport({\n title: p.title || undefined,\n success: p.success,\n operatorId: p.operatorId != null ? Number(p.operatorId) : undefined,\n path: p.path || undefined,\n createTime: p.createTime ?? null,\n columns: keys.join(','),\n })\n triggerBlobDownload(blob, t('logOp.fileName'))\n exportShow.value = false\n message.success(t('export.done'))\n } catch (e) {\n message.error(translateError(e))\n } finally {\n exporting.value = false\n }\n}\n\nconst columns: SmartTableColumn<SysOpLog>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n { key: 'title', title: () => t('log.opName'), search: true },\n { key: 'httpMethod', title: () => t('log.method'), width: 90 },\n { key: 'path', title: () => t('log.path'), ellipsis: { tooltip: true }, search: true },\n {\n key: 'success',\n title: () => t('log.result'),\n tag: true,\n search: true,\n options: [\n { label: () => t('log.success'), value: true, tagType: 'success' },\n { label: () => t('log.failed'), value: false, tagType: 'error' },\n ],\n },\n { key: 'resultCode', title: () => t('log.resultCode'), width: 100 },\n {\n key: 'operator',\n title: () => t('log.operator'),\n width: 120,\n render: r => operatorText(r),\n // 日志只存 OperatorId(姓名是读取时回填的),故按人筛必须是精确 Id → 复用现成的人员选择器,搜索键用 operatorId。\n search: {\n key: 'operatorId',\n label: () => t('log.operator'),\n render: ({ value, setValue, search }) =>\n h(UserSelect, {\n value: value as number | null,\n clearable: true,\n placeholder: t('log.operatorPlaceholder'),\n 'onUpdate:value': (v: number | null) => {\n setValue(v)\n search()\n },\n }),\n },\n },\n { key: 'elapsedMs', title: () => t('log.elapsed'), width: 100, render: r => `${r.elapsedMs} ms` },\n { key: 'ip', title: () => t('log.ip'), render: r => r.ip || '—' },\n // 时间范围是审计最常用的一刀(\"上周三下午谁删了那批数据\");daterange 回传 ['YYYY-MM-DD','YYYY-MM-DD'],\n // api 层拆成 StartTime/EndTime 并把结束日补到 23:59:59。\n {\n key: 'createTime',\n title: () => t('common.createTime'),\n format: 'datetime',\n search: { type: 'daterange' },\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 90,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openDetail(r) },\n () => t('log.detail'),\n ),\n },\n]\n\n// ── 详情抽屉(只读,行数据直填)──\nconst showDetail = ref(false)\nconst detailRow = ref<SysOpLog | null>(null)\nfunction openDetail(r: SysOpLog) {\n detailRow.value = r\n showDetail.value = true\n}\n/** paramJson 美化:parse→stringify(2);非法 JSON 原样返回。空值占位由模板判空。 */\nfunction prettyParam(json: string) {\n try {\n return JSON.stringify(JSON.parse(json), null, 2)\n } catch {\n return json\n }\n}\n\nfunction clearLogs() {\n confirm({\n type: 'error',\n content: t('log.clearOpConfirm'),\n action: () => logApi.opClear(),\n successMsg: t('log.cleared'),\n }).then(ok => {\n if (ok) tableRef.value?.refresh()\n })\n}\n</script>\n\n<template>\n <SmartTable\n :default-page-size=\"100\"\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"logApi.opPage\"\n flex-height\n virtual-scroll\n storage-key=\"sys-log-op\"\n @error=\"e => message.error(translateError(e))\"\n >\n <template #toolbar>\n <n-button v-auth=\"'DELETE:/api/v1/sys/log/op'\" type=\"error\" secondary @click=\"clearLogs\">\n <template #icon><AppIcon icon=\"ph:trash\" :size=\"16\" /></template>\n {{ t('log.clear') }}\n </n-button>\n <n-button v-auth=\"'GET:/api/v1/sys/log/op/export'\" @click=\"exportShow = true\">\n <template #icon><AppIcon icon=\"ph:download-simple\" :size=\"16\" /></template>\n {{ t('export.button') }}\n </n-button>\n </template>\n </SmartTable>\n\n <ExportColumnsModal\n v-model:show=\"exportShow\"\n :columns=\"opExportColumns\"\n :loading=\"exporting\"\n @confirm=\"onExport\"\n />\n\n <n-drawer v-model:show=\"showDetail\" :width=\"560\" placement=\"right\">\n <n-drawer-content :title=\"t('log.detail')\" closable>\n <n-descriptions v-if=\"detailRow\" label-placement=\"left\" :column=\"1\" bordered size=\"small\">\n <n-descriptions-item :label=\"t('log.opName')\">{{ detailRow.title }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.method')\">\n {{ detailRow.httpMethod }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.path')\">{{ detailRow.path }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.result')\">\n <n-tag :type=\"detailRow.success ? 'success' : 'error'\" size=\"small\" :bordered=\"false\">\n {{ detailRow.success ? t('log.success') : t('log.failed') }}\n </n-tag>\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.resultCode')\">\n {{ detailRow.resultCode }}\n </n-descriptions-item>\n <n-descriptions-item v-if=\"detailRow.exceptionMessage\" :label=\"t('log.exception')\">\n <pre class=\"exception\">{{ detailRow.exceptionMessage }}</pre>\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.elapsed')\">\n {{ detailRow.elapsedMs }} ms\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.operator')\">\n {{ operatorText(detailRow) }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.ip')\">{{ detailRow.ip || '—' }}</n-descriptions-item>\n <n-descriptions-item :label=\"t('log.userAgent')\">\n {{ detailRow.userAgent || '—' }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('common.createTime')\">\n {{ detailRow.createTime }}\n </n-descriptions-item>\n <n-descriptions-item :label=\"t('log.param')\">\n <CodeBlock v-if=\"detailRow.paramJson\" :code=\"prettyParam(detailRow.paramJson)\" />\n <span v-else>—</span>\n </n-descriptions-item>\n </n-descriptions>\n </n-drawer-content>\n </n-drawer>\n</template>\n\n<style scoped>\n.exception {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: 12px;\n line-height: 1.5;\n color: var(--color-danger);\n}\n</style>\n"],"names":["t","useI18n","message","useMessage","confirm","useConfirm","tableRef","ref","exportShow","exporting","opExportColumns","onExport","keys","p","_a","blob","logApi","triggerBlobDownload","e","translateError","columns","r","operatorText","value","setValue","search","h","UserSelect","v","NButton","openDetail","showDetail","detailRow","prettyParam","json","clearLogs","ok","_createVNode","_unref","SmartTable","_cache","_createBlock","AppIcon","_createTextVNode","ExportColumnsModal","$event","NDrawer","NDrawerContent","NDescriptions","NDescriptionsItem","_toDisplayString","NTag","_createElementVNode","_hoisted_1","CodeBlock","_openBlock","_createElementBlock"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import o from "./ButtonManager2.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-5c30c592"]]);
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-0a7963e1"]]);
5
5
  export {
6
6
  m as default
7
7
  };
@@ -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 {\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 { useAuthStore } from '#/stores/auth'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\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\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({ label: `${' '.repeat(depth)}${n.title}`, value: n.id })\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 = 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' },\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 '—'\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 { title: () => t('menu.sort'), key: 'sort', width: 70 },\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 fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, 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-space vertical :size=\"12\">\n <n-space>\n <n-button v-if=\"canAdd\" type=\"primary\" size=\"small\" @click=\"openAdd\">\n {{ t('menu.addButton') }}\n </n-button>\n <n-button v-if=\"canAdd\" size=\"small\" @click=\"openBatch\">\n {{ t('menu.batchFromRoutes') }}\n </n-button>\n </n-space>\n <SmartTable\n :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"buttons\"\n :pagination=\"false\"\n row-key=\"id\"\n storage-key=\"sys-menu-buttons\"\n />\n </n-space>\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 ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-placement=\"left\" :label-width=\"80\">\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\n v-for=\"r in filteredBatchRows\"\n :key=\"r.code\"\n style=\"display: flex; align-items: center; gap: 10px\"\n >\n <n-checkbox v-model:checked=\"r.checked\" />\n <span style=\"width: 210px; color: var(--color-text-secondary, #888); font-size: 12px\">\n {{ r.method }} {{ r.path }}\n </span>\n <n-input\n v-model:value=\"r.title\"\n size=\"small\"\n :disabled=\"!r.checked\"\n style=\"width: 160px\"\n />\n </div>\n </n-space>\n </n-space>\n </FormContainer>\n</template>\n\n<style scoped>\n.perm-hint {\n font-size: 12px;\n color: var(--color-text-tertiary, #999);\n line-height: 1.5;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","run","useConfirm","auth","useAuthStore","canAdd","computed","canEdit","canDelete","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","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","_createBlock","SmartTable","NForm","NGrid","NFormItemGi","NInput","_cache","NSelect","_createElementVNode","_hoisted_1","_toDisplayString","NCheckbox","NInputNumber","NSwitch","AppIcon","_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 {\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 { useAuthStore } from '#/stores/auth'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\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\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({ label: `${' '.repeat(depth)}${n.title}`, value: n.id })\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 = 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' },\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 '—'\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 { title: () => t('menu.sort'), key: 'sort', width: 70 },\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 fixed: 'right',\n render: r =>\n h(NSpace, { size: 2, 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-space vertical :size=\"12\">\n <n-space>\n <n-button v-if=\"canAdd\" type=\"primary\" size=\"small\" @click=\"openAdd\">\n {{ t('menu.addButton') }}\n </n-button>\n <n-button v-if=\"canAdd\" size=\"small\" @click=\"openBatch\">\n {{ t('menu.batchFromRoutes') }}\n </n-button>\n </n-space>\n <SmartTable\n :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"buttons\"\n :pagination=\"false\"\n row-key=\"id\"\n storage-key=\"sys-menu-buttons\"\n />\n </n-space>\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 ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-placement=\"left\" :label-width=\"80\">\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\n v-for=\"r in filteredBatchRows\"\n :key=\"r.code\"\n style=\"display: flex; align-items: center; gap: 10px\"\n >\n <n-checkbox v-model:checked=\"r.checked\" />\n <span style=\"width: 210px; color: var(--color-text-secondary, #888); font-size: 12px\">\n {{ r.method }} {{ r.path }}\n </span>\n <n-input\n v-model:value=\"r.title\"\n size=\"small\"\n :disabled=\"!r.checked\"\n style=\"width: 160px\"\n />\n </div>\n </n-space>\n </n-space>\n </FormContainer>\n</template>\n\n<style scoped>\n.perm-hint {\n font-size: 12px;\n color: var(--color-text-tertiary, #999);\n line-height: 1.5;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","run","useConfirm","auth","useAuthStore","canAdd","computed","canEdit","canDelete","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","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","_createBlock","SmartTable","NForm","NGrid","NFormItemGi","NInput","_cache","NSelect","_createElementVNode","_hoisted_1","_toDisplayString","NCheckbox","NInputNumber","NSwitch","AppIcon","_createElementBlock","_Fragment","_renderList","_hoisted_2","NEmpty"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import o from "./MenuFormModal2.vue.js";
2
2
  /* empty css */
3
- import a from "../../../../_virtual/_plugin-vue_export-helper.js";
4
- const e = /* @__PURE__ */ a(o, [["__scopeId", "data-v-b458eaa0"]]);
3
+ import r from "../../../../_virtual/_plugin-vue_export-helper.js";
4
+ const f = /* @__PURE__ */ r(o, [["__scopeId", "data-v-5b265f65"]]);
5
5
  export {
6
- e as default
6
+ f as default
7
7
  };
8
8
  //# sourceMappingURL=MenuFormModal.vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MenuFormModal2.vue.js","sources":["../../../../../src/views/system/menu/components/MenuFormModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 菜单「新增/编辑」弹窗 —— 从 menu/index.vue 抽出。父页拥有 tree/modules 数据并保持响应,\n// 本弹窗只读消费,保存后 emit('saved') 交回父页重拉树 + 重建壳层。\nimport { computed, reactive, ref } from 'vue'\nimport {\n NForm,\n NInput,\n NInputNumber,\n NSwitch,\n NSelect,\n NGrid,\n NFormItemGi,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport IconPicker from '#/components/IconPicker/index.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport { viewComponentPaths } from '#/composables/useAuthMenu'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { MenuType, type MenuInput, type MenuTreeNode, toMenuInput } from '#/types/menu'\nimport type { ModuleRow } from '#/types/api'\n\nconst props = defineProps<{\n modules: ModuleRow[]\n /** 全树:subtreeIds 防环需要按钮在内的完整结构。 */\n tree: MenuTreeNode[]\n /** 当前应用、已剥按钮的树:父级下拉的候选来源(只在本应用子树里选父级,防跨应用误挂)。 */\n filteredTree: MenuTreeNode[]\n moduleFilter: number\n}>()\nconst emit = defineEmits<{ (e: 'saved'): void }>()\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n/** 「未分配」「全部」哨兵,与父页一致:openAdd 顶级目录时据此决定是否自动带上当前应用。 */\nconst UNASSIGNED = 0\nconst ALL_MODULES = -1\n\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n // whitespace: 纯空白不算填写\n title: {\n required: true,\n whitespace: true,\n message: () => t('menu.titleRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst editingId = ref<number | null>(null)\nconst blank = (): MenuInput => ({\n parentId: 0,\n type: MenuType.Menu,\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\n/** 所属应用仅对顶级目录(parentId==0)有效——后端对子节点强制置空,前端据此隐藏字段。 */\nconst isTopLevel = computed(() => form.parentId === 0)\n\n// 类型只留目录/页面:按钮统一走「权限按钮」弹窗建,主表单不提供按钮选项。\nconst typeOptions = computed(() => [\n { label: t('menu.typeCatalog'), value: MenuType.Catalog },\n { label: t('menu.typeMenu'), value: MenuType.Menu },\n])\nconst moduleOptions = computed(() => props.modules.map(m => ({ label: m.title, value: m.id })))\n// 组件路径下拉:取自已注册的页面表(内核内置页 + 消费方注册的页面),选得到的一定存在;手填错误则由 MissingRoute 诊断。\nconst componentOptions = computed(() => viewComponentPaths().map(p => ({ label: p, value: p })))\n\n/** 收集以 id 为根的子树全部 id(含自身)——编辑时排除,防止把节点挂到自己的子孙下形成环。 */\nfunction subtreeIds(nodes: MenuTreeNode[], id: number): Set<number> {\n const out = new Set<number>()\n const find = (list: MenuTreeNode[]): MenuTreeNode | null => {\n for (const n of list) {\n if (n.id === id) return n\n const hit = find(n.children)\n if (hit) return hit\n }\n return null\n }\n const collect = (n: MenuTreeNode) => {\n out.add(n.id)\n n.children.forEach(collect)\n }\n const root = find(nodes)\n if (root) collect(root)\n return out\n}\n\n/** 父节点下拉:根 + 全部目录/页面(按钮不能当父),缩进体现层级;编辑时排除自身子树。 */\nconst parentOptions = computed(() => {\n const exclude =\n editingId.value === null ? new Set<number>() : subtreeIds(props.tree, editingId.value)\n const opts: { label: string; value: number }[] = [{ label: t('menu.parentRoot'), value: 0 }]\n const walk = (nodes: MenuTreeNode[], depth: number) => {\n for (const n of nodes) {\n if (n.type !== MenuType.Button && !exclude.has(n.id)) {\n opts.push({ label: `${' '.repeat(depth)}${n.title}`, value: n.id })\n }\n walk(n.children, depth + 1)\n }\n }\n // 只在当前应用的子树里选父级——防止把节点误挂到别的应用目录下(会改变其实际所属应用)。\n walk(props.filteredTree, 0)\n return opts\n})\n\nfunction openAdd(parentId = 0) {\n editingId.value = null\n // 新建顶级目录时,自动盖上当前筛选的应用(「未分配」「全部」筛选下则留空,交给用户手选)。\n const moduleId =\n parentId === 0 && props.moduleFilter !== UNASSIGNED && props.moduleFilter !== ALL_MODULES\n ? props.moduleFilter\n : null\n Object.assign(form, blank(), { parentId, moduleId })\n show.value = true\n}\nfunction openEdit(r: MenuTreeNode) {\n editingId.value = r.id\n Object.assign(form, toMenuInput(r))\n show.value = true\n}\ndefineExpose({ openAdd, openEdit })\n\n/** FormContainer onConfirm:校验失败 reject / API 失败 return false → 弹层不关;成功正常返回自动关。\n * 重拉树/重建壳层交回父页(emit),失败不该把已成功的保存报成失败。 */\nasync function save() {\n await formRef.value?.validate()\n try {\n const payload: MenuInput = { ...form, moduleId: isTopLevel.value ? form.moduleId : null }\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('saved')\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n</script>\n\n<template>\n <FormContainer\n v-model:show=\"show\"\n :title=\"editingId === null ? t('menu.addTitle') : t('menu.editTitle')\"\n :width=\"720\"\n :on-confirm=\"save\"\n :confirm-text=\"t('common.save')\"\n >\n <!-- 两列布局:短字段两两一行、长/特殊字段整行(span 2),省纵向空间免下拉。 -->\n <n-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-placement=\"left\" :label-width=\"90\">\n <n-grid :cols=\"2\" :x-gap=\"18\">\n <n-form-item-gi :label=\"t('menu.parent')\">\n <n-select v-model:value=\"form.parentId\" :options=\"parentOptions\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.type')\">\n <n-select v-model:value=\"form.type\" :options=\"typeOptions\" />\n </n-form-item-gi>\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-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 真正的权限码只活在按钮上,统一走每行「配置权限」按钮里的 ButtonManager 配。 -->\n <n-form-item-gi v-if=\"isTopLevel\" :span=\"2\" :label=\"t('menu.module')\">\n <n-select\n v-model:value=\"form.moduleId\"\n :options=\"moduleOptions\"\n clearable\n :placeholder=\"t('menu.moduleHint')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.path')\">\n <n-input v-model:value=\"form.path as string\" placeholder=\"/system/xxx | https://...\" />\n </n-form-item-gi>\n <!-- 组件路径:选项来自 import.meta.glob 的真实文件表,选得到的一定能加载;\n 手敲错则该菜单进入 MissingRoute 并输出 console.warn。tag 保留手敲(页面文件尚未创建时先占位)。\n 外链/iframe 约定见下方 linkHint:路径填 URL = 外链新窗口打开;组件填 URL = 内嵌 iframe。 -->\n <n-form-item-gi :label=\"t('menu.component')\">\n <n-select\n v-model:value=\"form.component as string\"\n :options=\"componentOptions\"\n filterable\n tag\n clearable\n placeholder=\"system/xxx/index | https://...\"\n />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"form.type === MenuType.Menu\" :span=\"2\" :show-label=\"false\">\n <span class=\"link-hint\">{{ t('menu.linkHint') }}</span>\n </n-form-item-gi>\n <n-form-item-gi :span=\"2\" :label=\"t('menu.icon')\">\n <IconPicker\n :model-value=\"form.icon ?? ''\"\n @update:model-value=\"(v: string) => (form.icon = v)\"\n />\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-form-item-gi :label=\"t('menu.visible')\">\n <n-switch v-model:value=\"form.visible\" />\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n.link-hint {\n font-size: 12px;\n color: var(--color-text-tertiary, #999);\n line-height: 1.5;\n}\n</style>\n"],"names":["UNASSIGNED","ALL_MODULES","props","__props","emit","__emit","t","useI18n","message","useMessage","show","ref","formRef","rules","editingId","blank","MenuType","form","reactive","isTopLevel","computed","typeOptions","moduleOptions","m","componentOptions","viewComponentPaths","p","subtreeIds","nodes","id","out","find","list","n","hit","collect","root","parentOptions","exclude","opts","walk","depth","openAdd","parentId","moduleId","openEdit","r","toMenuInput","__expose","save","_a","payload","menuApi","e","translateError","_createBlock","FormContainer","$event","_unref","_createVNode","NForm","NGrid","NFormItemGi","NSelect","_cache","NInput","NInputNumber","_createElementVNode","_hoisted_1","_toDisplayString","IconPicker","v","NSwitch"],"mappings":"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"}
1
+ {"version":3,"file":"MenuFormModal2.vue.js","sources":["../../../../../src/views/system/menu/components/MenuFormModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 菜单「新增/编辑」弹窗。父页(menu/index.vue)拥有 tree/modules 数据并保持响应,\n// 本弹窗只读消费,保存后 emit('saved') 交回父页重拉树 + 重建壳层。\nimport { computed, reactive, ref } from 'vue'\nimport {\n NForm,\n NInput,\n NInputNumber,\n NSwitch,\n NSelect,\n NGrid,\n NFormItemGi,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport IconPicker from '#/components/IconPicker/index.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport { viewComponentPaths } from '#/composables/useAuthMenu'\nimport { menuApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport { MenuType, type MenuInput, type MenuTreeNode, toMenuInput } from '#/types/menu'\nimport type { ModuleRow } from '#/types/api'\n\nconst props = defineProps<{\n modules: ModuleRow[]\n /** 全树:subtreeIds 防环需要按钮在内的完整结构。 */\n tree: MenuTreeNode[]\n /** 当前应用、已剥按钮的树:父级下拉的候选来源(只在本应用子树里选父级,防跨应用误挂)。 */\n filteredTree: MenuTreeNode[]\n moduleFilter: number\n}>()\nconst emit = defineEmits<{ (e: 'saved'): void }>()\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n/** 「未分配」「全部」哨兵,与父页一致:openAdd 顶级目录时据此决定是否自动带上当前应用。 */\nconst UNASSIGNED = 0\nconst ALL_MODULES = -1\n\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n // whitespace: 纯空白不算填写\n title: {\n required: true,\n whitespace: true,\n message: () => t('menu.titleRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst editingId = ref<number | null>(null)\nconst blank = (): MenuInput => ({\n parentId: 0,\n type: MenuType.Menu,\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\n/** 所属应用仅对顶级目录(parentId==0)有效——后端对子节点强制置空,前端据此隐藏字段。 */\nconst isTopLevel = computed(() => form.parentId === 0)\n\n// 类型只留目录/页面:按钮统一走「权限按钮」弹窗建,主表单不提供按钮选项。\nconst typeOptions = computed(() => [\n { label: t('menu.typeCatalog'), value: MenuType.Catalog },\n { label: t('menu.typeMenu'), value: MenuType.Menu },\n])\nconst moduleOptions = computed(() => props.modules.map(m => ({ label: m.title, value: m.id })))\n// 组件路径下拉:取自已注册的页面表(内核内置页 + 消费方注册的页面),选得到的一定存在;手填错误则由 MissingRoute 诊断。\nconst componentOptions = computed(() => viewComponentPaths().map(p => ({ label: p, value: p })))\n\n/** 收集以 id 为根的子树全部 id(含自身)——编辑时排除,防止把节点挂到自己的子孙下形成环。 */\nfunction subtreeIds(nodes: MenuTreeNode[], id: number): Set<number> {\n const out = new Set<number>()\n const find = (list: MenuTreeNode[]): MenuTreeNode | null => {\n for (const n of list) {\n if (n.id === id) return n\n const hit = find(n.children)\n if (hit) return hit\n }\n return null\n }\n const collect = (n: MenuTreeNode) => {\n out.add(n.id)\n n.children.forEach(collect)\n }\n const root = find(nodes)\n if (root) collect(root)\n return out\n}\n\n/** 父节点下拉:根 + 全部目录/页面(按钮不能当父),缩进体现层级;编辑时排除自身子树。 */\nconst parentOptions = computed(() => {\n const exclude =\n editingId.value === null ? new Set<number>() : subtreeIds(props.tree, editingId.value)\n const opts: { label: string; value: number }[] = [{ label: t('menu.parentRoot'), value: 0 }]\n const walk = (nodes: MenuTreeNode[], depth: number) => {\n for (const n of nodes) {\n if (n.type !== MenuType.Button && !exclude.has(n.id)) {\n opts.push({ label: `${' '.repeat(depth)}${n.title}`, value: n.id })\n }\n walk(n.children, depth + 1)\n }\n }\n // 只在当前应用的子树里选父级——防止把节点误挂到别的应用目录下(会改变其实际所属应用)。\n walk(props.filteredTree, 0)\n return opts\n})\n\nfunction openAdd(parentId = 0) {\n editingId.value = null\n // 新建顶级目录时,自动盖上当前筛选的应用(「未分配」「全部」筛选下则留空,交给用户手选)。\n const moduleId =\n parentId === 0 && props.moduleFilter !== UNASSIGNED && props.moduleFilter !== ALL_MODULES\n ? props.moduleFilter\n : null\n Object.assign(form, blank(), { parentId, moduleId })\n show.value = true\n}\nfunction openEdit(r: MenuTreeNode) {\n editingId.value = r.id\n Object.assign(form, toMenuInput(r))\n show.value = true\n}\ndefineExpose({ openAdd, openEdit })\n\n/** FormContainer onConfirm:校验失败 reject / API 失败 return false → 弹层不关;成功正常返回自动关。\n * 重拉树/重建壳层交回父页(emit),失败不该把已成功的保存报成失败。 */\nasync function save() {\n await formRef.value?.validate()\n try {\n const payload: MenuInput = { ...form, moduleId: isTopLevel.value ? form.moduleId : null }\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('saved')\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n</script>\n\n<template>\n <FormContainer\n v-model:show=\"show\"\n :title=\"editingId === null ? t('menu.addTitle') : t('menu.editTitle')\"\n :width=\"720\"\n :on-confirm=\"save\"\n :confirm-text=\"t('common.save')\"\n >\n <!-- 两列布局:短字段两两一行、长/特殊字段整行(span 2),省纵向空间免下拉。 -->\n <n-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-placement=\"left\" :label-width=\"90\">\n <n-grid :cols=\"2\" :x-gap=\"18\">\n <n-form-item-gi :label=\"t('menu.parent')\">\n <n-select v-model:value=\"form.parentId\" :options=\"parentOptions\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.type')\">\n <n-select v-model:value=\"form.type\" :options=\"typeOptions\" />\n </n-form-item-gi>\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-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 真正的权限码只活在按钮上,统一走每行「配置权限」按钮里的 ButtonManager 配。 -->\n <n-form-item-gi v-if=\"isTopLevel\" :span=\"2\" :label=\"t('menu.module')\">\n <n-select\n v-model:value=\"form.moduleId\"\n :options=\"moduleOptions\"\n clearable\n :placeholder=\"t('menu.moduleHint')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('menu.path')\">\n <n-input v-model:value=\"form.path as string\" placeholder=\"/system/xxx | https://...\" />\n </n-form-item-gi>\n <!-- 组件路径:选项来自已注册的页面表(内核内置页 + createSmartAdmin 传入的页面),选得到的一定能加载;\n 手敲错则该菜单进入 MissingRoute 并输出 console.warn。tag 保留手敲(页面文件尚未创建时先占位)。\n 外链/iframe 约定见下方 linkHint:路径填 URL = 外链新窗口打开;组件填 URL = 内嵌 iframe。 -->\n <n-form-item-gi :label=\"t('menu.component')\">\n <n-select\n v-model:value=\"form.component as string\"\n :options=\"componentOptions\"\n filterable\n tag\n clearable\n placeholder=\"system/xxx/index | https://...\"\n />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"form.type === MenuType.Menu\" :span=\"2\" :show-label=\"false\">\n <span class=\"link-hint\">{{ t('menu.linkHint') }}</span>\n </n-form-item-gi>\n <n-form-item-gi :span=\"2\" :label=\"t('menu.icon')\">\n <IconPicker\n :model-value=\"form.icon ?? ''\"\n @update:model-value=\"(v: string) => (form.icon = v)\"\n />\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-form-item-gi :label=\"t('menu.visible')\">\n <n-switch v-model:value=\"form.visible\" />\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n.link-hint {\n font-size: 12px;\n color: var(--color-text-tertiary, #999);\n line-height: 1.5;\n}\n</style>\n"],"names":["UNASSIGNED","ALL_MODULES","props","__props","emit","__emit","t","useI18n","message","useMessage","show","ref","formRef","rules","editingId","blank","MenuType","form","reactive","isTopLevel","computed","typeOptions","moduleOptions","m","componentOptions","viewComponentPaths","p","subtreeIds","nodes","id","out","find","list","n","hit","collect","root","parentOptions","exclude","opts","walk","depth","openAdd","parentId","moduleId","openEdit","r","toMenuInput","__expose","save","_a","payload","menuApi","e","translateError","_createBlock","FormContainer","$event","_unref","_createVNode","NForm","NGrid","NFormItemGi","NSelect","_cache","NInput","NInputNumber","_createElementVNode","_hoisted_1","_toDisplayString","IconPicker","v","NSwitch"],"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 f = /* @__PURE__ */ t(o, [["__scopeId", "data-v-dfe8710a"]]);
4
+ const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-d019bdd8"]]);
5
5
  export {
6
- f as default
6
+ p as default
7
7
  };
8
8
  //# sourceMappingURL=index.vue.js.map
@@ -125,7 +125,7 @@ const Te = { class: "view fill-page" }, b = 0, G = -1, Ve = /* @__PURE__ */ ae({
125
125
  align: "center",
126
126
  fixed: "left",
127
127
  hideInSetting: !0,
128
- // 结构性列,不进列设置(与原 type:'index' 的行为一致)
128
+ // 结构性列,不进列设置
129
129
  render: (e) => {
130
130
  const t = le.value.get(e.id);
131
131
  if (!t) return null;
@@ -142,7 +142,7 @@ const Te = { class: "view fill-page" }, b = 0, G = -1, Ve = /* @__PURE__ */ ae({
142
142
  align: "left",
143
143
  // 树列左对齐,展开缩进才好读
144
144
  // 展开箭头与每层 26px 缩进钉死在这一列。默认会落到「首个无 type 的列」= 序号列,
145
- // 于是缩进把 88px 里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。
145
+ // 于是缩进把序号列里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。
146
146
  tree: !0,
147
147
  minWidth: 240,
148
148
  // 让出让给缩进的宽度
@@ -152,12 +152,12 @@ const Te = { class: "view fill-page" }, b = 0, G = -1, Ve = /* @__PURE__ */ ae({
152
152
  // inline:必须行内 —— naive 的展开箭头是 inline-flex,默认块级 NSpace 会被挤到第二行。
153
153
  a(j, { inline: !0, align: "center", size: 6, wrapItem: !1 }, () => [
154
154
  e.title,
155
- // 「隐藏」是罕见状态(种子里 20/20 都是显示),只在为真时才占视觉 —— 省掉一整列同一个词。
155
+ // 「隐藏」是罕见状态(种子里的目录/页面全是显示),只在为真时才占视觉 —— 省掉一整列同一个词。
156
156
  e.visible ? null : a(K, { size: "tiny", bordered: !1, type: "warning" }, () => n("common.hidden"))
157
157
  ])
158
158
  )
159
159
  },
160
- // 图标独占一列(对齐参照实现):留在标题列会和树形展开箭头 + 缩进挤在一起撑破首列换行。
160
+ // 图标独占一列:留在标题列会和树形展开箭头 + 缩进挤在一起撑破首列换行。
161
161
  {
162
162
  title: () => n("menu.icon"),
163
163
  key: "icon",
@@ -165,7 +165,7 @@ const Te = { class: "view fill-page" }, b = 0, G = -1, Ve = /* @__PURE__ */ ae({
165
165
  align: "center",
166
166
  render: (e) => e.icon ? a(y, { icon: e.icon, size: 18 }) : null
167
167
  },
168
- // 类型独占一列(对齐参照实现),不塞在标题里。按类型着色,目录/页面一眼可辨。
168
+ // 类型独占一列,不塞在标题里。按类型着色,目录/页面一眼可辨。
169
169
  {
170
170
  title: () => n("menu.type"),
171
171
  key: "type",
@@ -177,7 +177,7 @@ const Te = { class: "view fill-page" }, b = 0, G = -1, Ve = /* @__PURE__ */ ae({
177
177
  )
178
178
  },
179
179
  // 路由/组件路径:管理员核对菜单时真正要看的东西,直接列出,不必点开编辑弹窗。
180
- // 不设权限码列 —— 树里只剩目录/页面,而它俩结构上永远没有权限码(种子 20/20 为空),
180
+ // 不设权限码列 —— 树里只有目录/页面,而它俩结构上永远没有权限码(种子里全为空),
181
181
  // 那一列必然是「—」。权限码的正主在「权限按钮」弹窗里。
182
182
  {
183
183
  title: () => n("menu.path"),