smart-admin-web 10.5.0 → 10.10.1
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.
- package/README.md +2 -1
- package/cli/icon-subset.mjs +132 -0
- package/cli/smart-admin-icons.mjs +57 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.js +2 -2
- package/dist/api/index.js.map +1 -1
- package/dist/components/DetailPage/index.vue.js +1 -1
- package/dist/components/DetailPage/index2.vue.js.map +1 -1
- package/dist/components/IconPicker/index.vue.js.map +1 -1
- package/dist/components/ImportWizard/index.vue.js +1 -1
- package/dist/components/ImportWizard/index2.vue.js.map +1 -1
- package/dist/components/TableZoomButton/index.vue.js.map +1 -1
- package/dist/composables/useConfirm.js.map +1 -1
- package/dist/composables/useLayoutMenu.js.map +1 -1
- package/dist/composables/useRealtime.d.ts +1 -1
- package/dist/composables/useRealtime.js.map +1 -1
- package/dist/createSmartAdmin.d.ts +5 -2
- package/dist/createSmartAdmin.js +33 -32
- package/dist/createSmartAdmin.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +257 -244
- package/dist/index.js.map +1 -1
- package/dist/layouts/default.vue.js +2 -2
- package/dist/layouts/default2.vue.js.map +1 -1
- package/dist/lib/icons.d.ts +5 -2
- package/dist/lib/icons.js +15 -13
- package/dist/lib/icons.js.map +1 -1
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/locales/en-US.js +1 -1
- package/dist/locales/en-US.js.map +1 -1
- package/dist/locales/zh-CN.js +2 -2
- package/dist/locales/zh-CN.js.map +1 -1
- package/dist/router/detailRoutes.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/routes.js +1 -1
- package/dist/router/routes.js.map +1 -1
- package/dist/stores/user.d.ts +2 -2
- package/dist/stores/user.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/theme/mix.d.ts +3 -3
- package/dist/theme/mix.js.map +1 -1
- package/dist/types/api.d.ts +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/utils/url.d.ts +2 -0
- package/dist/utils/url.js +9 -3
- package/dist/utils/url.js.map +1 -1
- package/dist/views/error/404.vue.js +2 -2
- package/dist/views/error/4042.vue.js.map +1 -1
- package/dist/views/login/skins/SplitPanel.vue.js +1 -1
- package/dist/views/login/skins/SplitPanel2.vue.js.map +1 -1
- package/dist/views/system/log/op/index.vue.js +1 -1
- package/dist/views/system/log/op/index2.vue.js +1 -1
- package/dist/views/system/log/op/index2.vue.js.map +1 -1
- package/dist/views/system/menu/components/ButtonManager.vue.js +1 -1
- package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
- package/dist/views/system/menu/components/MenuFormModal.vue.js +3 -3
- package/dist/views/system/menu/components/MenuFormModal2.vue.js.map +1 -1
- package/dist/views/system/menu/index.vue.js +2 -2
- package/dist/views/system/menu/index2.vue.js +6 -6
- package/dist/views/system/menu/index2.vue.js.map +1 -1
- package/dist/views/system/module/index.vue.js +2 -2
- package/dist/views/system/module/index2.vue.js +2 -2
- package/dist/views/system/module/index2.vue.js.map +1 -1
- package/dist/views/system/user/components/ResetPasswordModal.vue.js +1 -1
- package/dist/views/system/user/components/ResetPasswordModal2.vue.js.map +1 -1
- package/dist/views/system/user/components/UserFormModal.vue.js +1 -1
- package/dist/views/system/user/components/UserFormModal2.vue.js.map +1 -1
- package/package.json +6 -2
|
@@ -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, watch } from 'vue'\nimport { NButton, NSpace, NTag, NInput, NSelect, NDropdown, useMessage } from 'naive-ui'\nimport { SmartTable, type SmartTableColumn } 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 { expandableIds, filterTree } from '#/utils/tree'\nimport { MenuType, type MenuTreeNode, splitPermission, toMenuInput } from '#/types/menu'\nimport type { ModuleRow, PermissionRouteItem } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst auth = useAuthStore()\n\nconst loading = ref(false)\n// 整份替换 + 只读(行内改动一律重拉 load(),不往节点上写值),不需要深响应\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\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}\nonMounted(async () => {\n await load()\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// 统一淡 tag,不做三色区分:按钮已不在树里,只剩目录/页面两种,而这个区别树形缩进和\n// 「有没有路由路径」已经说清楚了;每行都顶一坨颜色反而是噪音(参照实现同样选了无色)。\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(() => modules.value.map(m => ({ label: m.title, value: m.id })))\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// 内核开箱就播了 90 行菜单,全展开、没搜索、没折叠 = 第一天打开就糊脸。\n// SmartTable 静态 data 模式不做任何前端过滤(它只在 fetcher 模式下发请求),所以过滤得自己算;\n// 关键字放 #toolbar 而不是用列的 search 配置:树表没有分页,搜索卡片会白占一整块高度。\n// 只喂给表格,不喂给 parentOptions —— 父级下拉必须始终是完整的当前应用子树,不能随搜索缩水。\nconst keyword = ref('')\nconst visibleTree = computed(() => {\n const kw = keyword.value.trim().toLowerCase()\n if (!kw) return filteredTree.value\n // 注意别写成 n.permission:过滤跑在已剥掉按钮的 filteredTree 上,而目录/页面的 permission 恒为空,\n // 那样写等于永不命中。要按权限码搜,只能查该节点「按钮子节点」的码(见 buttonInfoById)。\n return filterTree(\n filteredTree.value,\n n =>\n n.title.toLowerCase().includes(kw) ||\n (n.path ?? '').toLowerCase().includes(kw) ||\n (buttonInfoById.value.get(n.id)?.perms ?? '').includes(kw),\n )\n})\n\n// 展开受控:一旦传了 expanded-row-keys,naive 就以它为准(default-expand-all 会被初始值直接覆盖)。\n// 初值留空 = 进来全折叠:先给一屏顶级目录的骨架,要看哪一支自己点开;\n// 90 行菜单一进门就全摊开,反而得先滚半天才找得到自己要改的那条。\nconst expandedKeys = ref<number[]>([])\n\n// 搜索是唯一必须自动展开的场景:filterTree 命中的节点可能埋在任意深度,祖先还折着就等于搜了个寂寞。\n// 结果每变一次重播一次(收窄关键字也算);无关键字时一律不碰 —— 见下面那条 watch 的注释。\nwatch(visibleTree, next => {\n if (keyword.value.trim()) expandedKeys.value = expandableIds(next)\n})\n\n// 关键字清空 → 回到默认的全折叠;切应用 = 换了一棵树,展开态同样归位。\n// 这两件事必须挂在「关键字/应用」上,不能写进上面 watch(visibleTree) 里无条件重置:\n// 保存菜单后会重拉树,那样用户手点开的行会被一起按回去,改完一条就迷路一次。\nwatch([() => keyword.value.trim(), moduleFilter], ([kw]) => {\n if (!kw) expandedKeys.value = []\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([load(), 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) Promise.all([load(), syncShell()])\n })\n\n// 展开图标:内核默认三角太淡、展开态朝下,辨识度低。统一成固定尺寸的「>」caret,展开时旋转 90°\n// —— 观感清晰(对齐参照实现),尺寸恒定,行高不会因默认图标忽大忽小而参差。\nconst renderExpandIcon = ({ expanded }: { expanded: boolean }) =>\n h(\n 'span',\n {\n style: `display:inline-flex;transition:transform .2s;transform:rotate(${expanded ? 90 : 0}deg)`,\n },\n h(AppIcon, { icon: 'ph:caret-right-bold', size: 13 }),\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\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, // 结构性列,不进列设置(与原 type:'index' 的行为一致)\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 align: 'left', // 树列左对齐,展开缩进才好读\n // 展开箭头与每层 26px 缩进钉死在这一列。默认会落到「首个无 type 的列」= 序号列,\n // 于是缩进把 88px 里的数字一层层挤出可视区(展开后序号就看不见了);缩进也只有紧贴标题才读得通。\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 r.title,\n // 「隐藏」是罕见状态(种子里 20/20 都是显示),只在为真时才占视觉 —— 省掉一整列同一个词。\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: 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 // 不设权限码列 —— 树里只剩目录/页面,而它俩结构上永远没有权限码(种子 20/20 为空),\n // 那一列必然是「—」。权限码的正主在「权限按钮」弹窗里。\n {\n title: () => t('menu.path'),\n key: 'path',\n align: 'left',\n width: 160,\n ellipsis: { tooltip: true },\n render: r => r.path || '—',\n },\n {\n title: () => t('menu.component'),\n key: 'component',\n align: 'left',\n width: 180,\n ellipsis: { tooltip: true },\n render: r => 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 { 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 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 load()\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 moreOptions = [\n auth.hasPerm('POST:/api/v1/sys/menu/add') && {\n key: 'addChild',\n label: t('menu.addChild'),\n },\n auth.hasPerm('DELETE:/api/v1/sys/menu/{id}') && {\n key: 'delete',\n label: t('common.delete'),\n },\n ].filter(Boolean) as { key: string; label: string }[]\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 () => h(NButton, { size: 'small', quaternary: true }, () => t('common.more')),\n )\n : null,\n ]\n }),\n },\n]\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 :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"visibleTree\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n storage-key=\"sys-menu\"\n :indent=\"26\"\n flex-height\n virtual-scroll\n :min-row-height=\"48\"\n :render-expand-icon=\"renderExpandIcon\"\n :expanded-row-keys=\"expandedKeys\"\n @update:expanded-row-keys=\"(keys: number[]) => (expandedKeys = keys)\"\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 <n-input\n v-model:value=\"keyword\"\n clearable\n :placeholder=\"t('menu.searchPlaceholder')\"\n style=\"width: 220px\"\n >\n <template #prefix><AppIcon icon=\"ph:magnifying-glass\" :size=\"16\" /></template>\n </n-input>\n <n-button quaternary @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(--color-text-disabled);\n}\n\n:deep(.row-no__self) {\n color: var(--color-text-secondary);\n font-weight: var(--font-weight-medium);\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","m","stripButtons","nodes","n","filteredTree","r","buttonInfoById","walk","btns","c","b","splitPermission","keyword","visibleTree","kw","filterTree","expandedKeys","watch","next","expandableIds","allExpanded","toggleExpandAll","currentAppPrefix","menuFormRef","buttonMgrRef","openButtons","reloadTree","onDelete","ok","renderExpandIcon","expanded","h","AppIcon","rowNoById","prefix","i","no","columns","cut","NSpace","NTag","NButton","StatusSwitch","toMenuInput","moreOptions","NDropdown","key","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","_cache","keys","NSelect","$event","NInput","_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, watch } from 'vue'\nimport { NButton, NSpace, NTag, NInput, NSelect, NDropdown, useMessage } from 'naive-ui'\nimport { SmartTable, type SmartTableColumn } 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 { expandableIds, filterTree } from '#/utils/tree'\nimport { MenuType, type MenuTreeNode, splitPermission, toMenuInput } from '#/types/menu'\nimport type { ModuleRow, PermissionRouteItem } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\nconst auth = useAuthStore()\n\nconst loading = ref(false)\n// 整份替换 + 只读(行内改动一律重拉 load(),不往节点上写值),不需要深响应\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\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}\nonMounted(async () => {\n await load()\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(() => modules.value.map(m => ({ label: m.title, value: m.id })))\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// SmartTable 静态 data 模式不做任何前端过滤(它只在 fetcher 模式下发请求),所以过滤得自己算;\n// 关键字放 #toolbar 而不是用列的 search 配置:树表没有分页,搜索卡片会白占一整块高度。\n// 只喂给表格,不喂给 parentOptions —— 父级下拉必须始终是完整的当前应用子树,不能随搜索缩水。\nconst keyword = ref('')\nconst visibleTree = computed(() => {\n const kw = keyword.value.trim().toLowerCase()\n if (!kw) return filteredTree.value\n // 注意别写成 n.permission:过滤跑在已剥掉按钮的 filteredTree 上,而目录/页面的 permission 恒为空,\n // 那样写等于永不命中。要按权限码搜,只能查该节点「按钮子节点」的码(见 buttonInfoById)。\n return filterTree(\n filteredTree.value,\n n =>\n n.title.toLowerCase().includes(kw) ||\n (n.path ?? '').toLowerCase().includes(kw) ||\n (buttonInfoById.value.get(n.id)?.perms ?? '').includes(kw),\n )\n})\n\n// 展开受控:一旦传了 expanded-row-keys,naive 就以它为准(default-expand-all 会被初始值直接覆盖)。\n// 初值留空 = 进来全折叠:先给一屏顶级目录的骨架,要看哪一支自己点开;\n// 整棵树一进门就全摊开,反而得先滚半天才找得到自己要改的那条。\nconst expandedKeys = ref<number[]>([])\n\n// 搜索是唯一必须自动展开的场景:filterTree 命中的节点可能埋在任意深度,祖先还折着就等于搜了个寂寞。\n// 结果每变一次重播一次(收窄关键字也算);无关键字时一律不碰 —— 见下面那条 watch 的注释。\nwatch(visibleTree, next => {\n if (keyword.value.trim()) expandedKeys.value = expandableIds(next)\n})\n\n// 关键字清空 → 回到默认的全折叠;切应用 = 换了一棵树,展开态同样归位。\n// 这两件事必须挂在「关键字/应用」上,不能写进上面 watch(visibleTree) 里无条件重置:\n// 保存菜单后会重拉树,那样用户手点开的行会被一起按回去,改完一条就迷路一次。\nwatch([() => keyword.value.trim(), moduleFilter], ([kw]) => {\n if (!kw) expandedKeys.value = []\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([load(), 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) Promise.all([load(), syncShell()])\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;transform:rotate(${expanded ? 90 : 0}deg)`,\n },\n h(AppIcon, { icon: 'ph:caret-right-bold', size: 13 }),\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\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 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 r.title,\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: 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 => r.path || '—',\n },\n {\n title: () => t('menu.component'),\n key: 'component',\n align: 'left',\n width: 180,\n ellipsis: { tooltip: true },\n render: r => 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 { 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 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 load()\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 moreOptions = [\n auth.hasPerm('POST:/api/v1/sys/menu/add') && {\n key: 'addChild',\n label: t('menu.addChild'),\n },\n auth.hasPerm('DELETE:/api/v1/sys/menu/{id}') && {\n key: 'delete',\n label: t('common.delete'),\n },\n ].filter(Boolean) as { key: string; label: string }[]\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 () => h(NButton, { size: 'small', quaternary: true }, () => t('common.more')),\n )\n : null,\n ]\n }),\n },\n]\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 :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"visibleTree\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n storage-key=\"sys-menu\"\n :indent=\"26\"\n flex-height\n virtual-scroll\n :min-row-height=\"48\"\n :render-expand-icon=\"renderExpandIcon\"\n :expanded-row-keys=\"expandedKeys\"\n @update:expanded-row-keys=\"(keys: number[]) => (expandedKeys = keys)\"\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 <n-input\n v-model:value=\"keyword\"\n clearable\n :placeholder=\"t('menu.searchPlaceholder')\"\n style=\"width: 220px\"\n >\n <template #prefix><AppIcon icon=\"ph:magnifying-glass\" :size=\"16\" /></template>\n </n-input>\n <n-button quaternary @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(--color-text-disabled);\n}\n\n:deep(.row-no__self) {\n color: var(--color-text-secondary);\n font-weight: var(--font-weight-medium);\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","m","stripButtons","nodes","n","filteredTree","r","buttonInfoById","walk","btns","c","b","splitPermission","keyword","visibleTree","kw","filterTree","expandedKeys","watch","next","expandableIds","allExpanded","toggleExpandAll","currentAppPrefix","menuFormRef","buttonMgrRef","openButtons","reloadTree","onDelete","ok","renderExpandIcon","expanded","h","AppIcon","rowNoById","prefix","i","no","columns","cut","NSpace","NTag","NButton","StatusSwitch","toMenuInput","moreOptions","NDropdown","key","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","_cache","keys","NSelect","$event","NInput","_createTextVNode","_toDisplayString","_createBlock","MenuFormModal","ButtonManager"],"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
|
|
4
|
+
const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-ea6672c5"]]);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
m as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=index.vue.js.map
|
|
@@ -27,7 +27,7 @@ const ue = { class: "hint" }, ke = /* @__PURE__ */ $({
|
|
|
27
27
|
}
|
|
28
28
|
A(v);
|
|
29
29
|
const b = m(!1), _ = m(null), q = {
|
|
30
|
-
// whitespace:
|
|
30
|
+
// whitespace: 纯空白不算填写
|
|
31
31
|
code: {
|
|
32
32
|
required: !0,
|
|
33
33
|
whitespace: !0,
|
|
@@ -106,7 +106,7 @@ const ue = { class: "hint" }, ke = /* @__PURE__ */ $({
|
|
|
106
106
|
render: (e) => g.hasPerm("PUT:/api/v1/sys/module/{id}") ? n(ae, {
|
|
107
107
|
value: e.enabled,
|
|
108
108
|
// 内置 system 应用停用会让门户失联(模块/菜单管理页都在它下面,停了就没法从 UI 恢复)——
|
|
109
|
-
//
|
|
109
|
+
// 与「禁删」同级的前端保护;后端停用与删除内置模块都拒绝(42013),这里只是不给点。
|
|
110
110
|
disabled: h(e),
|
|
111
111
|
// 停用会把应用从门户隐藏,先确认;启用无副作用,跳过确认(返回 null)。
|
|
112
112
|
confirm: (a) => a ? null : t("module.disableConfirm", { title: e.title }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/module/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { h, onMounted, reactive, ref } from 'vue'\nimport {\n NButton,\n NSpace,\n NTag,\n NForm,\n NFormItem,\n NInput,\n NInputNumber,\n NSwitch,\n NPopconfirm,\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 IconPicker from '#/components/IconPicker/index.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 { moduleApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { ModuleInput, ModuleRow } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst authStore = useAuthStore()\n\nconst loading = ref(false)\nconst rows = ref<ModuleRow[]>([])\n\n/** 内置 system 模块受保护(后端 42013),前端据 code 禁删,避免明知不可为的请求。 */\nconst isBuiltin = (r: ModuleRow) => r.code === 'system'\n\nasync function load() {\n loading.value = true\n try {\n rows.value = await moduleApi.list()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\nonMounted(load)\n\n// ── 新增/编辑弹窗(FormContainer:loading/底栏/关闭时机由容器按 onConfirm 协议接管)──\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n // whitespace: 保留原「.trim() 禁用保存」的语义,纯空白不算填写\n code: {\n required: true,\n whitespace: true,\n message: () => t('module.codeRequired'),\n trigger: ['input', 'blur'],\n },\n title: {\n required: true,\n whitespace: true,\n message: () => t('module.nameRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst editingId = ref<number | null>(null)\nconst blank = (): ModuleInput => ({\n code: '',\n title: '',\n icon: '',\n defaultRoute: '',\n apiPrefix: '',\n sort: 0,\n enabled: true,\n remark: '',\n})\nconst form = reactive<ModuleInput>(blank())\n\n/** 行数据 → 完整入参:openEdit 回填与 StatusSwitch 行内改状态共用(后端无独立启停端点,均走全量 update)。 */\nconst toInput = (r: ModuleRow): ModuleInput => ({\n code: r.code,\n title: r.title,\n icon: r.icon ?? '',\n defaultRoute: r.defaultRoute ?? '',\n apiPrefix: r.apiPrefix ?? '',\n sort: r.sort,\n enabled: r.enabled,\n remark: r.remark ?? '',\n})\n\nfunction openAdd() {\n editingId.value = null\n Object.assign(form, blank())\n show.value = true\n}\nfunction openEdit(r: ModuleRow) {\n editingId.value = r.id\n Object.assign(form, toInput(r))\n show.value = true\n}\n\n/** FormContainer onConfirm:校验失败 reject / API 失败 return false → 弹层不关;成功正常返回自动关。 */\nasync function save() {\n await formRef.value?.validate()\n try {\n if (editingId.value === null) await moduleApi.add({ ...form })\n else await moduleApi.update(editingId.value, { ...form })\n message.success(t('module.saved'))\n await load()\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n\nconst columns: SmartTableColumn<ModuleRow>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n { title: () => t('module.code'), key: 'code' },\n {\n title: () => t('module.name'),\n key: 'title',\n render: r =>\n h(NSpace, { align: 'center', size: 6, wrapItem: false }, () => [\n r.icon ? h(AppIcon, { icon: r.icon, size: 18 }) : null,\n r.title,\n isBuiltin(r)\n ? h(NTag, { size: 'small', type: 'info', bordered: false }, () => t('module.builtin'))\n : null,\n ]),\n },\n {\n title: () => t('module.defaultRoute'),\n key: 'defaultRoute',\n render: r => r.defaultRoute || '—',\n },\n {\n title: () => t('module.apiPrefix'),\n key: 'apiPrefix',\n width: 120,\n render: r => r.apiPrefix || '—',\n },\n { title: () => t('module.sort'), key: 'sort', width: 80 },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 90,\n // 无启停(update)权限者不见开关,退化为只读状态标签,保留状态可见性。\n render: r =>\n authStore.hasPerm('PUT:/api/v1/sys/module/{id}')\n ? h(StatusSwitch, {\n value: r.enabled,\n // 内置 system 应用停用会让门户失联(模块/菜单管理页都在它下面,停了就没法从 UI 恢复)——\n // 与「禁删」同级的前端保护;后端 UpdateAsync 暂未拦内置模块,只护删除(42013)。\n disabled: isBuiltin(r),\n // 停用会把应用从门户隐藏,先确认;启用无副作用,跳过确认(返回 null)。\n confirm: (next: boolean) =>\n next ? null : t('module.disableConfirm', { title: r.title }),\n request: (next: boolean) => moduleApi.update(r.id, { ...toInput(r), enabled: next }),\n 'onUpdate:value': (v: boolean) => {\n r.enabled = v\n },\n })\n : h(NTag, { size: 'small', bordered: false, type: r.enabled ? 'success' : 'default' }, () =>\n t(r.enabled ? 'common.enabled' : 'common.disabled'),\n ),\n },\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 140,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(NSpace, { size: 4, wrapItem: false }, () => [\n authStore.hasPerm('PUT:/api/v1/sys/module/{id}')\n ? h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openEdit(r) },\n () => t('common.edit'),\n )\n : null,\n isBuiltin(r) || !authStore.hasPerm('DELETE:/api/v1/sys/module/{id}')\n ? null\n : h(\n NPopconfirm,\n {\n // popconfirm 留在模板层当触发器,「执行→toast」后半段交给 useConfirm().run。\n onPositiveClick: () =>\n run(() => moduleApi.remove(r.id), t('module.deleted')).then(ok => {\n if (ok) load()\n }),\n },\n {\n trigger: () =>\n h(NButton, { size: 'small', quaternary: true, type: 'error' }, () =>\n t('common.delete'),\n ),\n default: () => t('module.deleteConfirm', { title: r.title }),\n },\n ),\n ]),\n },\n]\n</script>\n\n<template>\n <!-- 静态数据模式:module 只有全量 list 端点,没有分页端点 —— 内置刷新按钮在无 fetcher 时是空转,\n 故关掉它自己接一个。 -->\n <SmartTable\n :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"rows\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n :toolbar=\"{ refresh: false }\"\n flex-height\n virtual-scroll\n storage-key=\"sys-module\"\n >\n <template #toolbar>\n <n-button quaternary :loading=\"loading\" @click=\"load\">\n <template #icon><AppIcon icon=\"ph:arrow-clockwise\" :size=\"16\" /></template>\n </n-button>\n <n-button v-auth=\"'POST:/api/v1/sys/module/add'\" type=\"primary\" @click=\"openAdd\">\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('common.add') }}\n </n-button>\n </template>\n </SmartTable>\n\n <FormContainer\n v-model:show=\"show\"\n :title=\"editingId === null ? t('module.addTitle') : t('module.editTitle')\"\n :width=\"520\"\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=\"90\">\n <n-form-item :label=\"t('module.code')\" path=\"code\">\n <n-input\n v-model:value=\"form.code\"\n :placeholder=\"t('module.code')\"\n :disabled=\"editingId !== null\"\n />\n </n-form-item>\n <n-form-item :label=\"t('module.name')\" path=\"title\">\n <n-input v-model:value=\"form.title\" :placeholder=\"t('module.name')\" />\n </n-form-item>\n <n-form-item :label=\"t('module.icon')\">\n <IconPicker\n :model-value=\"form.icon ?? ''\"\n @update:model-value=\"(v: string) => (form.icon = v)\"\n />\n </n-form-item>\n <n-form-item :label=\"t('module.defaultRoute')\">\n <n-input v-model:value=\"form.defaultRoute as string\" placeholder=\"/system/user\" />\n </n-form-item>\n <n-form-item :label=\"t('module.apiPrefix')\">\n <n-space vertical :size=\"2\" style=\"width: 100%\">\n <n-input v-model:value=\"form.apiPrefix as string\" placeholder=\"sys\" />\n <span class=\"hint\">{{ t('module.apiPrefixHint') }}</span>\n </n-space>\n </n-form-item>\n <n-form-item :label=\"t('module.sort')\">\n <n-input-number v-model:value=\"form.sort\" :min=\"0\" style=\"width: 160px\" />\n </n-form-item>\n <n-form-item :label=\"t('common.status')\">\n <n-switch v-model:value=\"form.enabled\" />\n </n-form-item>\n <n-form-item :label=\"t('module.remark')\">\n <n-input v-model:value=\"form.remark as string\" type=\"textarea\" :autosize=\"{ minRows: 2 }\" />\n </n-form-item>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n.hint {\n font-size: 12px;\n color: var(--color-text-secondary, #999);\n}\n</style>\n"],"names":["useI18n","message","useMessage","run","useConfirm","authStore","useAuthStore","loading","ref","rows","isBuiltin","r","load","moduleApi","translateError","onMounted","show","formRef","rules","editingId","blank","form","reactive","toInput","openAdd","openEdit","save","_a","e","columns","h","NSpace","AppIcon","NTag","StatusSwitch","next","v","NButton","NPopconfirm","ok","_createVNode","_unref","SmartTable","_createBlock","_createTextVNode","FormContainer","$event","NForm","NFormItem","NInput","_cache","IconPicker","_createElementVNode","_hoisted_1","_toDisplayString","NInputNumber","NSwitch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/module/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { h, onMounted, reactive, ref } from 'vue'\nimport {\n NButton,\n NSpace,\n NTag,\n NForm,\n NFormItem,\n NInput,\n NInputNumber,\n NSwitch,\n NPopconfirm,\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 IconPicker from '#/components/IconPicker/index.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 { moduleApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { ModuleInput, ModuleRow } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst authStore = useAuthStore()\n\nconst loading = ref(false)\nconst rows = ref<ModuleRow[]>([])\n\n/** 内置 system 模块受保护(后端 42013),前端据 code 禁删,避免明知不可为的请求。 */\nconst isBuiltin = (r: ModuleRow) => r.code === 'system'\n\nasync function load() {\n loading.value = true\n try {\n rows.value = await moduleApi.list()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\nonMounted(load)\n\n// ── 新增/编辑弹窗(FormContainer:loading/底栏/关闭时机由容器按 onConfirm 协议接管)──\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst rules: FormRules = {\n // whitespace: 纯空白不算填写\n code: {\n required: true,\n whitespace: true,\n message: () => t('module.codeRequired'),\n trigger: ['input', 'blur'],\n },\n title: {\n required: true,\n whitespace: true,\n message: () => t('module.nameRequired'),\n trigger: ['input', 'blur'],\n },\n}\nconst editingId = ref<number | null>(null)\nconst blank = (): ModuleInput => ({\n code: '',\n title: '',\n icon: '',\n defaultRoute: '',\n apiPrefix: '',\n sort: 0,\n enabled: true,\n remark: '',\n})\nconst form = reactive<ModuleInput>(blank())\n\n/** 行数据 → 完整入参:openEdit 回填与 StatusSwitch 行内改状态共用(后端无独立启停端点,均走全量 update)。 */\nconst toInput = (r: ModuleRow): ModuleInput => ({\n code: r.code,\n title: r.title,\n icon: r.icon ?? '',\n defaultRoute: r.defaultRoute ?? '',\n apiPrefix: r.apiPrefix ?? '',\n sort: r.sort,\n enabled: r.enabled,\n remark: r.remark ?? '',\n})\n\nfunction openAdd() {\n editingId.value = null\n Object.assign(form, blank())\n show.value = true\n}\nfunction openEdit(r: ModuleRow) {\n editingId.value = r.id\n Object.assign(form, toInput(r))\n show.value = true\n}\n\n/** FormContainer onConfirm:校验失败 reject / API 失败 return false → 弹层不关;成功正常返回自动关。 */\nasync function save() {\n await formRef.value?.validate()\n try {\n if (editingId.value === null) await moduleApi.add({ ...form })\n else await moduleApi.update(editingId.value, { ...form })\n message.success(t('module.saved'))\n await load()\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\n\nconst columns: SmartTableColumn<ModuleRow>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n { title: () => t('module.code'), key: 'code' },\n {\n title: () => t('module.name'),\n key: 'title',\n render: r =>\n h(NSpace, { align: 'center', size: 6, wrapItem: false }, () => [\n r.icon ? h(AppIcon, { icon: r.icon, size: 18 }) : null,\n r.title,\n isBuiltin(r)\n ? h(NTag, { size: 'small', type: 'info', bordered: false }, () => t('module.builtin'))\n : null,\n ]),\n },\n {\n title: () => t('module.defaultRoute'),\n key: 'defaultRoute',\n render: r => r.defaultRoute || '—',\n },\n {\n title: () => t('module.apiPrefix'),\n key: 'apiPrefix',\n width: 120,\n render: r => r.apiPrefix || '—',\n },\n { title: () => t('module.sort'), key: 'sort', width: 80 },\n {\n title: () => t('common.status'),\n key: 'enabled',\n width: 90,\n // 无启停(update)权限者不见开关,退化为只读状态标签,保留状态可见性。\n render: r =>\n authStore.hasPerm('PUT:/api/v1/sys/module/{id}')\n ? h(StatusSwitch, {\n value: r.enabled,\n // 内置 system 应用停用会让门户失联(模块/菜单管理页都在它下面,停了就没法从 UI 恢复)——\n // 与「禁删」同级的前端保护;后端停用与删除内置模块都拒绝(42013),这里只是不给点。\n disabled: isBuiltin(r),\n // 停用会把应用从门户隐藏,先确认;启用无副作用,跳过确认(返回 null)。\n confirm: (next: boolean) =>\n next ? null : t('module.disableConfirm', { title: r.title }),\n request: (next: boolean) => moduleApi.update(r.id, { ...toInput(r), enabled: next }),\n 'onUpdate:value': (v: boolean) => {\n r.enabled = v\n },\n })\n : h(NTag, { size: 'small', bordered: false, type: r.enabled ? 'success' : 'default' }, () =>\n t(r.enabled ? 'common.enabled' : 'common.disabled'),\n ),\n },\n {\n title: () => t('common.operation'),\n key: 'op',\n width: 140,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(NSpace, { size: 4, wrapItem: false }, () => [\n authStore.hasPerm('PUT:/api/v1/sys/module/{id}')\n ? h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openEdit(r) },\n () => t('common.edit'),\n )\n : null,\n isBuiltin(r) || !authStore.hasPerm('DELETE:/api/v1/sys/module/{id}')\n ? null\n : h(\n NPopconfirm,\n {\n // popconfirm 留在模板层当触发器,「执行→toast」后半段交给 useConfirm().run。\n onPositiveClick: () =>\n run(() => moduleApi.remove(r.id), t('module.deleted')).then(ok => {\n if (ok) load()\n }),\n },\n {\n trigger: () =>\n h(NButton, { size: 'small', quaternary: true, type: 'error' }, () =>\n t('common.delete'),\n ),\n default: () => t('module.deleteConfirm', { title: r.title }),\n },\n ),\n ]),\n },\n]\n</script>\n\n<template>\n <!-- 静态数据模式:module 只有全量 list 端点,没有分页端点 —— 内置刷新按钮在无 fetcher 时是空转,\n 故关掉它自己接一个。 -->\n <SmartTable\n :default-page-size=\"100\"\n :columns=\"columns\"\n :data=\"rows\"\n :loading=\"loading\"\n row-key=\"id\"\n :pagination=\"false\"\n :toolbar=\"{ refresh: false }\"\n flex-height\n virtual-scroll\n storage-key=\"sys-module\"\n >\n <template #toolbar>\n <n-button quaternary :loading=\"loading\" @click=\"load\">\n <template #icon><AppIcon icon=\"ph:arrow-clockwise\" :size=\"16\" /></template>\n </n-button>\n <n-button v-auth=\"'POST:/api/v1/sys/module/add'\" type=\"primary\" @click=\"openAdd\">\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('common.add') }}\n </n-button>\n </template>\n </SmartTable>\n\n <FormContainer\n v-model:show=\"show\"\n :title=\"editingId === null ? t('module.addTitle') : t('module.editTitle')\"\n :width=\"520\"\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=\"90\">\n <n-form-item :label=\"t('module.code')\" path=\"code\">\n <n-input\n v-model:value=\"form.code\"\n :placeholder=\"t('module.code')\"\n :disabled=\"editingId !== null\"\n />\n </n-form-item>\n <n-form-item :label=\"t('module.name')\" path=\"title\">\n <n-input v-model:value=\"form.title\" :placeholder=\"t('module.name')\" />\n </n-form-item>\n <n-form-item :label=\"t('module.icon')\">\n <IconPicker\n :model-value=\"form.icon ?? ''\"\n @update:model-value=\"(v: string) => (form.icon = v)\"\n />\n </n-form-item>\n <n-form-item :label=\"t('module.defaultRoute')\">\n <n-input v-model:value=\"form.defaultRoute as string\" placeholder=\"/system/user\" />\n </n-form-item>\n <n-form-item :label=\"t('module.apiPrefix')\">\n <n-space vertical :size=\"2\" style=\"width: 100%\">\n <n-input v-model:value=\"form.apiPrefix as string\" placeholder=\"sys\" />\n <span class=\"hint\">{{ t('module.apiPrefixHint') }}</span>\n </n-space>\n </n-form-item>\n <n-form-item :label=\"t('module.sort')\">\n <n-input-number v-model:value=\"form.sort\" :min=\"0\" style=\"width: 160px\" />\n </n-form-item>\n <n-form-item :label=\"t('common.status')\">\n <n-switch v-model:value=\"form.enabled\" />\n </n-form-item>\n <n-form-item :label=\"t('module.remark')\">\n <n-input v-model:value=\"form.remark as string\" type=\"textarea\" :autosize=\"{ minRows: 2 }\" />\n </n-form-item>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n.hint {\n font-size: 12px;\n color: var(--color-text-secondary, #999);\n}\n</style>\n"],"names":["useI18n","message","useMessage","run","useConfirm","authStore","useAuthStore","loading","ref","rows","isBuiltin","r","load","moduleApi","translateError","onMounted","show","formRef","rules","editingId","blank","form","reactive","toInput","openAdd","openEdit","save","_a","e","columns","h","NSpace","AppIcon","NTag","StatusSwitch","next","v","NButton","NPopconfirm","ok","_createVNode","_unref","SmartTable","_createBlock","_createTextVNode","FormContainer","$event","NForm","NFormItem","NInput","_cache","IconPicker","_createElementVNode","_hoisted_1","_toDisplayString","NInputNumber","NSwitch"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import o from "./ResetPasswordModal2.vue.js";
|
|
2
2
|
/* empty css */
|
|
3
3
|
import t from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
-
const d = /* @__PURE__ */ t(o, [["__scopeId", "data-v-
|
|
4
|
+
const d = /* @__PURE__ */ t(o, [["__scopeId", "data-v-104f8da0"]]);
|
|
5
5
|
export {
|
|
6
6
|
d as default
|
|
7
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResetPasswordModal2.vue.js","sources":["../../../../../src/views/system/user/components/ResetPasswordModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 重置密码弹层 +
|
|
1
|
+
{"version":3,"file":"ResetPasswordModal2.vue.js","sources":["../../../../../src/views/system/user/components/ResetPasswordModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 重置密码弹层 + 初始密码结果只读弹层,父页是 user/index.vue。\n// 结果弹层被两条路径共用:重置密码后展示新口令,以及新建用户留空口令时展示系统随机口令(父页经 showResult 转交)。\n// 语义相同:明文只此一次,管理员当场转达。\nimport { reactive, ref } from 'vue'\nimport { NButton, NInput, NForm, NFormItem, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport { userApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { UserItem } from '#/types/api'\n\nconst { t } = useI18n()\nconst message = useMessage()\n\nconst showReset = ref(false)\nconst resetTarget = ref<UserItem | null>(null)\nconst resetForm = reactive({ newPassword: '' })\nconst showResetResult = ref(false)\nconst resetResult = ref('')\n/** 结果弹层是\"建号后\"还是\"重置后\"打开的,只影响标题文案。 */\nconst resultFromCreate = ref(false)\n\nfunction openReset(r: UserItem) {\n resetTarget.value = r\n resetForm.newPassword = ''\n showReset.value = true\n}\n/** 供新建留空口令路径复用同一结果弹层(父页在 UserFormModal 的 passwordGenerated 回调里调用)。 */\nfunction showResult(pwd: string, fromCreate = true) {\n resetResult.value = pwd\n resultFromCreate.value = fromCreate\n showResetResult.value = true\n}\ndefineExpose({ openReset, showResult })\n\nasync function doReset() {\n if (!resetTarget.value) return\n try {\n resetResult.value = await userApi.resetPassword(\n resetTarget.value.id,\n resetForm.newPassword || null,\n )\n resultFromCreate.value = false\n showResetResult.value = true // 关闭输入弹层、弹出结果\n } catch (e) {\n message.error(translateError(e))\n return false\n }\n}\nasync function copyResult() {\n try {\n await navigator.clipboard.writeText(resetResult.value)\n message.success(t('user.copied'))\n } catch {\n message.error(t('user.copyFailed'))\n }\n}\n</script>\n\n<template>\n <!-- 重置密码输入 -->\n <FormContainer\n v-model:show=\"showReset\"\n :title=\"t('user.resetPassword')\"\n :width=\"420\"\n :on-confirm=\"doReset\"\n :confirm-text=\"t('common.confirm')\"\n >\n <n-form :model=\"resetForm\" label-placement=\"left\" :label-width=\"90\">\n <n-form-item :label=\"t('user.newPassword')\">\n <n-input\n v-model:value=\"resetForm.newPassword\"\n type=\"password\"\n show-password-on=\"click\"\n :placeholder=\"t('user.newPasswordHint')\"\n />\n </n-form-item>\n </n-form>\n </FormContainer>\n\n <!-- 初始密码结果(只读,可复制):建号留空口令 / 重置密码 共用 -->\n <FormContainer\n v-model:show=\"showResetResult\"\n :title=\"resultFromCreate ? t('user.createDone') : t('user.resetDone')\"\n :width=\"420\"\n :on-confirm=\"() => {}\"\n :confirm-text=\"t('common.confirm')\"\n >\n <p class=\"reset-hint\">{{ t('user.resetDoneHint') }}</p>\n <n-input :value=\"resetResult\" readonly>\n <template #suffix>\n <n-button text type=\"primary\" @click=\"copyResult\">{{ t('user.copy') }}</n-button>\n </template>\n </n-input>\n </FormContainer>\n</template>\n\n<style scoped>\n.reset-hint {\n margin: 0 0 12px;\n font-size: var(--font-size-sm, 13px);\n color: var(--color-text-secondary, #888);\n}\n</style>\n"],"names":["useI18n","message","useMessage","showReset","ref","resetTarget","resetForm","reactive","showResetResult","resetResult","resultFromCreate","openReset","r","showResult","pwd","fromCreate","__expose","doReset","userApi","e","translateError","copyResult","_createVNode","FormContainer","$event","_unref","NForm","NFormItem","NInput","_cache","_createElementVNode","_hoisted_1","_toDisplayString","NButton"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import o from "./UserFormModal2.vue.js";
|
|
2
2
|
/* empty css */
|
|
3
3
|
import r from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
-
const m = /* @__PURE__ */ r(o, [["__scopeId", "data-v-
|
|
4
|
+
const m = /* @__PURE__ */ r(o, [["__scopeId", "data-v-f2afa7e7"]]);
|
|
5
5
|
export {
|
|
6
6
|
m as default
|
|
7
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserFormModal2.vue.js","sources":["../../../../../src/views/system/user/components/UserFormModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 用户「新增/编辑」弹窗 —— 从 user/index.vue 抽出。职位/角色/主管下拉由父页拉好经 props 传入(避免重复请求);\n// 编辑回显走 userApi.detail 拿 roleIds;保存后 emit('saved') 交回父页刷新表格。\n// 新增留空口令时后端生成随机强口令,经 emit('passwordGenerated') 交回父页用同一结果弹层展示(明文仅此一次)。\nimport { reactive, ref } from 'vue'\nimport {\n NButton,\n NAvatar,\n NInput,\n NForm,\n NFormItemGi,\n NGrid,\n NSelect,\n NSwitch,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport PasswordStrength from '#/components/PasswordStrength/index.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport OrgTreeSelect from '#/components/OrgTreeSelect/index.vue'\nimport DictSelect from '#/components/DictSelect/index.vue'\nimport FileUpload from '#/components/FileUpload/index.vue'\nimport { userApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { AddUserInput, UpdateUserInput, UserItem } from '#/types/api'\n\nconst props = defineProps<{\n positionOptions: { label: string; value: number }[]\n roleOptions: { label: string; value: number }[]\n directorOptions: { label: string; value: number }[]\n}>()\nconst emit = defineEmits<{\n (e: 'saved'): void\n (e: 'passwordGenerated', pwd: string): void\n}>()\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n// 头像首字母兜底(列表页亦有一份同名工具,各自 3 字符不值得上提)。\nconst initial = (name?: string | null) => (name || '?').slice(0, 1)\n\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst editingId = ref<number | null>(null)\nconst rules: FormRules = {\n account: {\n required: true,\n whitespace: true,\n message: () => t('user.accountRequired'),\n trigger: ['input', 'blur'],\n },\n name: {\n required: true,\n whitespace: true,\n message: () => t('user.nameRequired'),\n trigger: ['input', 'blur'],\n },\n // 手机/邮箱选填,填了才校验格式(前端信任边界;后端存自由字符串)\n phone: {\n validator: (_r, v: string) => !v || /^1[3-9]\\d{9}$/.test(v),\n message: () => t('user.phoneInvalid'),\n trigger: ['input', 'blur'],\n },\n email: {\n validator: (_r, v: string) => !v || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(v),\n message: () => t('user.emailInvalid'),\n trigger: ['input', 'blur'],\n },\n}\ninterface UserForm {\n account: string\n password: string\n name: string\n nickname: string\n phone: string\n email: string\n gender: string | null\n avatar: string | null\n orgId: number | null\n positionId: number | null\n directorId: number | null\n enabled: boolean\n forceTotp: boolean\n totpEnabled: boolean\n roleIds: number[]\n}\nconst blank = (): UserForm => ({\n account: '',\n password: '',\n name: '',\n nickname: '',\n phone: '',\n email: '',\n gender: null,\n avatar: null,\n orgId: null,\n positionId: null,\n directorId: null,\n enabled: true,\n forceTotp: false,\n totpEnabled: false,\n roleIds: [],\n})\nconst form = reactive<UserForm>(blank())\nconst avatarUploading = ref(false) // 头像上传中:给上传按钮加 spinner,别让用户对着空白干等\n\nfunction openAdd() {\n editingId.value = null\n Object.assign(form, blank())\n show.value = true\n}\n/** 编辑:先取 detail(拿 roleIds + 回显),成功再开弹层;失败弹码不开。 */\nasync function openEdit(r: UserItem) {\n try {\n const d = await userApi.detail(r.id)\n editingId.value = r.id\n Object.assign(form, {\n account: d.account,\n password: '',\n name: d.name,\n nickname: d.nickname ?? '',\n phone: d.phone ?? '',\n email: d.email ?? '',\n gender: d.gender ?? null,\n avatar: d.avatar ?? null,\n orgId: d.orgId ?? null,\n positionId: d.positionId ?? null,\n directorId: d.directorId ?? null,\n enabled: d.enabled,\n forceTotp: !!d.forceTotp,\n totpEnabled: !!d.totpEnabled,\n roleIds: d.roleIds ?? [],\n })\n show.value = true\n } catch (e) {\n message.error(translateError(e))\n }\n}\ndefineExpose({ openAdd, openEdit })\n\nasync function save() {\n await formRef.value?.validate()\n try {\n if (editingId.value === null) {\n const body: AddUserInput = {\n account: form.account,\n password: form.password || undefined, // 留空 → 省略字段 → 后端生成随机强口令(明文经出参回传)\n name: form.name,\n nickname: form.nickname || null,\n phone: form.phone || null,\n email: form.email || null,\n gender: form.gender,\n avatar: form.avatar,\n orgId: form.orgId,\n positionId: form.positionId,\n directorId: form.directorId,\n enabled: form.enabled,\n forceTotp: form.forceTotp,\n roleIds: form.roleIds,\n }\n const out = await userApi.add(body)\n // 管理员没自己指定口令时,系统生成的随机口令只有这一次机会展示——不弹出来这个号谁也登不进去。\n // 交回父页用重置密码那套只读+复制弹层(同一语义:明文仅供管理员当场转达)。\n if (!form.password) emit('passwordGenerated', out.initialPassword)\n } else {\n const body: UpdateUserInput = {\n name: form.name,\n nickname: form.nickname || null,\n phone: form.phone || null,\n email: form.email || null,\n gender: form.gender,\n avatar: form.avatar,\n orgId: form.orgId,\n positionId: form.positionId,\n directorId: form.directorId,\n enabled: form.enabled,\n forceTotp: form.forceTotp,\n roleIds: form.roleIds,\n }\n await userApi.update(editingId.value, body)\n }\n message.success(t('user.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('user.addTitle') : t('user.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=\"96\">\n <n-grid :cols=\"2\" :x-gap=\"16\">\n <n-form-item-gi\n :span=\"editingId === null ? 1 : 2\"\n :label=\"t('user.account')\"\n path=\"account\"\n >\n <n-input\n v-model:value=\"form.account\"\n :disabled=\"editingId !== null\"\n :placeholder=\"t('user.account')\"\n />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"editingId === null\" :label=\"t('user.password')\">\n <div class=\"pw-field\">\n <n-input\n v-model:value=\"form.password\"\n type=\"password\"\n show-password-on=\"click\"\n :placeholder=\"t('user.passwordHint')\"\n />\n <PasswordStrength :value=\"form.password\" />\n </div>\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.name')\" path=\"name\">\n <n-input v-model:value=\"form.name\" :placeholder=\"t('user.name')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.nickname')\">\n <n-input v-model:value=\"form.nickname\" :placeholder=\"t('user.nicknamePlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.gender')\">\n <DictSelect\n v-model:value=\"form.gender\"\n type-code=\"gender\"\n clearable\n :placeholder=\"t('user.genderPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.phone')\" path=\"phone\">\n <n-input v-model:value=\"form.phone\" :placeholder=\"t('user.phonePlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.email')\" path=\"email\">\n <n-input v-model:value=\"form.email\" :placeholder=\"t('user.emailPlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.org')\">\n <OrgTreeSelect v-model:value=\"form.orgId\" :placeholder=\"t('user.orgPlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.position')\">\n <n-select\n v-model:value=\"form.positionId\"\n :options=\"props.positionOptions\"\n clearable\n :placeholder=\"t('user.positionPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.director')\">\n <n-select\n v-model:value=\"form.directorId\"\n :options=\"props.directorOptions\"\n clearable\n filterable\n :placeholder=\"t('user.directorPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.roles')\">\n <n-select\n v-model:value=\"form.roleIds\"\n :options=\"props.roleOptions\"\n multiple\n clearable\n :placeholder=\"t('user.rolesPlaceholder')\"\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('user.forceTotp')\">\n <n-switch v-model:value=\"form.forceTotp\" />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"editingId !== null\" :label=\"t('user.totpBound')\">\n <n-switch :value=\"form.totpEnabled\" disabled />\n </n-form-item-gi>\n <n-form-item-gi :span=\"2\" :label=\"t('user.avatar')\">\n <div class=\"avatar-field\">\n <n-avatar round :size=\"48\" style=\"flex-shrink: 0\" :src=\"form.avatar || undefined\">\n <template #fallback>{{ initial(form.name) }}</template>\n <template v-if=\"!form.avatar\">{{ initial(form.name) }}</template>\n </n-avatar>\n <FileUpload\n accept=\"image/*\"\n :show-file-list=\"false\"\n @loading-change=\"v => (avatarUploading = v)\"\n @uploaded=\"o => (form.avatar = o.viewUrl ?? null)\"\n >\n <n-button size=\"small\" :loading=\"avatarUploading\" :disabled=\"avatarUploading\">\n <template #icon><AppIcon icon=\"ph:upload-simple\" :size=\"14\" /></template>\n {{ avatarUploading ? t('user.avatarUploading') : t('user.avatar') }}\n </n-button>\n </FileUpload>\n </div>\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n/* 密码框 + 强度条竖排(表单为 label-left 横排,此格内需纵向堆叠) */\n.pw-field {\n flex: 1;\n min-width: 0;\n}\n/* 头像:当前预览 + 上传按钮横排 */\n.avatar-field {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","initial","name","show","ref","formRef","editingId","rules","_r","v","blank","form","reactive","avatarUploading","openAdd","openEdit","r","d","userApi","e","translateError","__expose","save","_a","body","out","_createBlock","FormContainer","$event","_unref","_createVNode","NForm","NGrid","NFormItemGi","NInput","_cache","_createElementVNode","_hoisted_1","PasswordStrength","DictSelect","OrgTreeSelect","NSelect","NSwitch","_hoisted_2","NAvatar","_createElementBlock","_Fragment","FileUpload","o","NButton","AppIcon","_createTextVNode","_toDisplayString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserFormModal2.vue.js","sources":["../../../../../src/views/system/user/components/UserFormModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 用户「新增/编辑」弹窗。职位/角色/主管下拉由父页(user/index.vue)拉好经 props 传入(避免重复请求);\n// 编辑回显走 userApi.detail 拿 roleIds;保存后 emit('saved') 交回父页刷新表格。\n// 新增留空口令时后端生成随机强口令,经 emit('passwordGenerated') 交回父页用同一结果弹层展示(明文仅此一次)。\nimport { reactive, ref } from 'vue'\nimport {\n NButton,\n NAvatar,\n NInput,\n NForm,\n NFormItemGi,\n NGrid,\n NSelect,\n NSwitch,\n useMessage,\n type FormInst,\n type FormRules,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport PasswordStrength from '#/components/PasswordStrength/index.vue'\nimport FormContainer from '#/components/FormContainer/index.vue'\nimport OrgTreeSelect from '#/components/OrgTreeSelect/index.vue'\nimport DictSelect from '#/components/DictSelect/index.vue'\nimport FileUpload from '#/components/FileUpload/index.vue'\nimport { userApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { AddUserInput, UpdateUserInput, UserItem } from '#/types/api'\n\nconst props = defineProps<{\n positionOptions: { label: string; value: number }[]\n roleOptions: { label: string; value: number }[]\n directorOptions: { label: string; value: number }[]\n}>()\nconst emit = defineEmits<{\n (e: 'saved'): void\n (e: 'passwordGenerated', pwd: string): void\n}>()\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n// 头像首字母兜底(列表页亦有一份同名工具,各自 3 字符不值得上提)。\nconst initial = (name?: string | null) => (name || '?').slice(0, 1)\n\nconst show = ref(false)\nconst formRef = ref<FormInst | null>(null)\nconst editingId = ref<number | null>(null)\nconst rules: FormRules = {\n account: {\n required: true,\n whitespace: true,\n message: () => t('user.accountRequired'),\n trigger: ['input', 'blur'],\n },\n name: {\n required: true,\n whitespace: true,\n message: () => t('user.nameRequired'),\n trigger: ['input', 'blur'],\n },\n // 手机/邮箱选填,填了才校验格式(前端信任边界;后端存自由字符串)\n phone: {\n validator: (_r, v: string) => !v || /^1[3-9]\\d{9}$/.test(v),\n message: () => t('user.phoneInvalid'),\n trigger: ['input', 'blur'],\n },\n email: {\n validator: (_r, v: string) => !v || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(v),\n message: () => t('user.emailInvalid'),\n trigger: ['input', 'blur'],\n },\n}\ninterface UserForm {\n account: string\n password: string\n name: string\n nickname: string\n phone: string\n email: string\n gender: string | null\n avatar: string | null\n orgId: number | null\n positionId: number | null\n directorId: number | null\n enabled: boolean\n forceTotp: boolean\n totpEnabled: boolean\n roleIds: number[]\n}\nconst blank = (): UserForm => ({\n account: '',\n password: '',\n name: '',\n nickname: '',\n phone: '',\n email: '',\n gender: null,\n avatar: null,\n orgId: null,\n positionId: null,\n directorId: null,\n enabled: true,\n forceTotp: false,\n totpEnabled: false,\n roleIds: [],\n})\nconst form = reactive<UserForm>(blank())\nconst avatarUploading = ref(false) // 头像上传中:给上传按钮加 spinner,别让用户对着空白干等\n\nfunction openAdd() {\n editingId.value = null\n Object.assign(form, blank())\n show.value = true\n}\n/** 编辑:先取 detail(拿 roleIds + 回显),成功再开弹层;失败弹码不开。 */\nasync function openEdit(r: UserItem) {\n try {\n const d = await userApi.detail(r.id)\n editingId.value = r.id\n Object.assign(form, {\n account: d.account,\n password: '',\n name: d.name,\n nickname: d.nickname ?? '',\n phone: d.phone ?? '',\n email: d.email ?? '',\n gender: d.gender ?? null,\n avatar: d.avatar ?? null,\n orgId: d.orgId ?? null,\n positionId: d.positionId ?? null,\n directorId: d.directorId ?? null,\n enabled: d.enabled,\n forceTotp: !!d.forceTotp,\n totpEnabled: !!d.totpEnabled,\n roleIds: d.roleIds ?? [],\n })\n show.value = true\n } catch (e) {\n message.error(translateError(e))\n }\n}\ndefineExpose({ openAdd, openEdit })\n\nasync function save() {\n await formRef.value?.validate()\n try {\n if (editingId.value === null) {\n const body: AddUserInput = {\n account: form.account,\n password: form.password || undefined, // 留空 → 省略字段 → 后端生成随机强口令(明文经出参回传)\n name: form.name,\n nickname: form.nickname || null,\n phone: form.phone || null,\n email: form.email || null,\n gender: form.gender,\n avatar: form.avatar,\n orgId: form.orgId,\n positionId: form.positionId,\n directorId: form.directorId,\n enabled: form.enabled,\n forceTotp: form.forceTotp,\n roleIds: form.roleIds,\n }\n const out = await userApi.add(body)\n // 管理员没自己指定口令时,系统生成的随机口令只有这一次机会展示——不弹出来这个号谁也登不进去。\n // 交回父页用重置密码那套只读+复制弹层(同一语义:明文仅供管理员当场转达)。\n if (!form.password) emit('passwordGenerated', out.initialPassword)\n } else {\n const body: UpdateUserInput = {\n name: form.name,\n nickname: form.nickname || null,\n phone: form.phone || null,\n email: form.email || null,\n gender: form.gender,\n avatar: form.avatar,\n orgId: form.orgId,\n positionId: form.positionId,\n directorId: form.directorId,\n enabled: form.enabled,\n forceTotp: form.forceTotp,\n roleIds: form.roleIds,\n }\n await userApi.update(editingId.value, body)\n }\n message.success(t('user.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('user.addTitle') : t('user.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=\"96\">\n <n-grid :cols=\"2\" :x-gap=\"16\">\n <n-form-item-gi\n :span=\"editingId === null ? 1 : 2\"\n :label=\"t('user.account')\"\n path=\"account\"\n >\n <n-input\n v-model:value=\"form.account\"\n :disabled=\"editingId !== null\"\n :placeholder=\"t('user.account')\"\n />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"editingId === null\" :label=\"t('user.password')\">\n <div class=\"pw-field\">\n <n-input\n v-model:value=\"form.password\"\n type=\"password\"\n show-password-on=\"click\"\n :placeholder=\"t('user.passwordHint')\"\n />\n <PasswordStrength :value=\"form.password\" />\n </div>\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.name')\" path=\"name\">\n <n-input v-model:value=\"form.name\" :placeholder=\"t('user.name')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.nickname')\">\n <n-input v-model:value=\"form.nickname\" :placeholder=\"t('user.nicknamePlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.gender')\">\n <DictSelect\n v-model:value=\"form.gender\"\n type-code=\"gender\"\n clearable\n :placeholder=\"t('user.genderPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.phone')\" path=\"phone\">\n <n-input v-model:value=\"form.phone\" :placeholder=\"t('user.phonePlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.email')\" path=\"email\">\n <n-input v-model:value=\"form.email\" :placeholder=\"t('user.emailPlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.org')\">\n <OrgTreeSelect v-model:value=\"form.orgId\" :placeholder=\"t('user.orgPlaceholder')\" />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.position')\">\n <n-select\n v-model:value=\"form.positionId\"\n :options=\"props.positionOptions\"\n clearable\n :placeholder=\"t('user.positionPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.director')\">\n <n-select\n v-model:value=\"form.directorId\"\n :options=\"props.directorOptions\"\n clearable\n filterable\n :placeholder=\"t('user.directorPlaceholder')\"\n />\n </n-form-item-gi>\n <n-form-item-gi :label=\"t('user.roles')\">\n <n-select\n v-model:value=\"form.roleIds\"\n :options=\"props.roleOptions\"\n multiple\n clearable\n :placeholder=\"t('user.rolesPlaceholder')\"\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('user.forceTotp')\">\n <n-switch v-model:value=\"form.forceTotp\" />\n </n-form-item-gi>\n <n-form-item-gi v-if=\"editingId !== null\" :label=\"t('user.totpBound')\">\n <n-switch :value=\"form.totpEnabled\" disabled />\n </n-form-item-gi>\n <n-form-item-gi :span=\"2\" :label=\"t('user.avatar')\">\n <div class=\"avatar-field\">\n <n-avatar round :size=\"48\" style=\"flex-shrink: 0\" :src=\"form.avatar || undefined\">\n <template #fallback>{{ initial(form.name) }}</template>\n <template v-if=\"!form.avatar\">{{ initial(form.name) }}</template>\n </n-avatar>\n <FileUpload\n accept=\"image/*\"\n :show-file-list=\"false\"\n @loading-change=\"v => (avatarUploading = v)\"\n @uploaded=\"o => (form.avatar = o.viewUrl ?? null)\"\n >\n <n-button size=\"small\" :loading=\"avatarUploading\" :disabled=\"avatarUploading\">\n <template #icon><AppIcon icon=\"ph:upload-simple\" :size=\"14\" /></template>\n {{ avatarUploading ? t('user.avatarUploading') : t('user.avatar') }}\n </n-button>\n </FileUpload>\n </div>\n </n-form-item-gi>\n </n-grid>\n </n-form>\n </FormContainer>\n</template>\n\n<style scoped>\n/* 密码框 + 强度条竖排(表单为 label-left 横排,此格内需纵向堆叠) */\n.pw-field {\n flex: 1;\n min-width: 0;\n}\n/* 头像:当前预览 + 上传按钮横排 */\n.avatar-field {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","message","useMessage","initial","name","show","ref","formRef","editingId","rules","_r","v","blank","form","reactive","avatarUploading","openAdd","openEdit","r","d","userApi","e","translateError","__expose","save","_a","body","out","_createBlock","FormContainer","$event","_unref","_createVNode","NForm","NGrid","NFormItemGi","NInput","_cache","_createElementVNode","_hoisted_1","PasswordStrength","DictSelect","OrgTreeSelect","NSelect","NSwitch","_hoisted_2","NAvatar","_createElementBlock","_Fragment","FileUpload","o","NButton","AppIcon","_createTextVNode","_toDisplayString"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smart-admin-web",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.10.1",
|
|
4
4
|
"description": "SmartAdmin 前端内核:布局、路由、鉴权、内置系统页与共享组件,配合 SmartAdmin NuGet 包使用",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"smartadmin",
|
|
@@ -24,8 +24,12 @@
|
|
|
24
24
|
"**/*.css"
|
|
25
25
|
],
|
|
26
26
|
"files": [
|
|
27
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"cli"
|
|
28
29
|
],
|
|
30
|
+
"bin": {
|
|
31
|
+
"smart-admin-icons": "./cli/smart-admin-icons.mjs"
|
|
32
|
+
},
|
|
29
33
|
"main": "./dist/index.js",
|
|
30
34
|
"module": "./dist/index.js",
|
|
31
35
|
"types": "./dist/index.d.ts",
|