From aaf8a50511d77dbc209ca93bbba308c21179a8bc Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期二, 31 三月 2026 15:38:47 +0800
Subject: [PATCH] #前端

---
 rsf-design/src/views/system/menu/index.vue |  323 +++++++----------------------------------------------
 1 files changed, 45 insertions(+), 278 deletions(-)

diff --git a/rsf-design/src/views/system/menu/index.vue b/rsf-design/src/views/system/menu/index.vue
index 2bb409c..e53a4dd 100644
--- a/rsf-design/src/views/system/menu/index.vue
+++ b/rsf-design/src/views/system/menu/index.vue
@@ -51,8 +51,7 @@
   import MenuDialog from './modules/menu-dialog.vue'
 
   import { formatMenuTitle } from '@/utils/router'
-  import ArtSvgIcon from '@/components/core/base/art-svg-icon/index.vue'
-  import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
+  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
   import { useTableColumns } from '@/hooks/core/useTableColumns'
   import {
     fetchDeleteMenu,
@@ -60,7 +59,16 @@
     fetchSaveMenu,
     fetchUpdateMenu
   } from '@/api/system-manage'
-  import { ElTag, ElMessage, ElMessageBox } from 'element-plus'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { createMenuTableColumns } from './menuTable.columns'
+  import {
+    buildMenuSubmitPayload,
+    buildMenuTreeOptions,
+    createMenuSearchState,
+    expandMenuAuthChildren,
+    filterMenuTree,
+    getMenuDisplayTitle
+  } from './menuPage.helpers'
 
   defineOptions({ name: 'Menus' })
 
@@ -74,10 +82,7 @@
   const tableData = ref([])
   const menuTreeOptions = ref([])
 
-  const initialSearchState = {
-    name: '',
-    route: ''
-  }
+  const initialSearchState = createMenuSearchState()
 
   const formFilters = reactive({ ...initialSearchState })
   const appliedFilters = reactive({ ...initialSearchState })
@@ -97,39 +102,19 @@
     }
   ])
 
-  const normalizeNumber = (value, fallback = 0) => {
-    if (value === '' || value === null || value === undefined) {
-      return fallback
-    }
-    const normalized = Number(value)
-    return Number.isNaN(normalized) ? fallback : normalized
-  }
-
-  const normalizeMenuTreeOptions = (nodes = []) => {
-    if (!Array.isArray(nodes)) {
-      return []
-    }
-
-    return nodes
-      .map((node) => ({
-        label: formatMenuTitle(node.meta?.title || node.name || ''),
-        value: normalizeNumber(node.id, 0),
-        children: normalizeMenuTreeOptions(node.children)
-      }))
-  }
-
   const loadMenuResources = async () => {
     loading.value = true
     try {
-      const list = await fetchGetMenuList({})
+      const list = await guardRequestWithMessage(fetchGetMenuList({}), null, {
+        timeoutMessage: '鑿滃崟鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+      })
+      if (list === null) {
+        tableData.value = []
+        menuTreeOptions.value = []
+        return
+      }
       tableData.value = Array.isArray(list) ? list : []
-      menuTreeOptions.value = [
-        {
-          label: '椤剁骇鑿滃崟',
-          value: 0,
-          children: normalizeMenuTreeOptions(tableData.value)
-        }
-      ]
+      menuTreeOptions.value = buildMenuTreeOptions(tableData.value, formatMenuTitle)
     } catch (error) {
       ElMessage.error(error?.message || '鑾峰彇鑿滃崟澶辫触')
     } finally {
@@ -141,237 +126,35 @@
     loadMenuResources()
   })
 
-  const hasNestedMenus = (row) => Array.isArray(row.children) && row.children.some((child) => !child.meta?.isAuthButton)
-
-  const getMenuTypeTag = (row) => {
-    if (row.meta?.isAuthButton || Number(row.type) === 1) return 'danger'
-    if (hasNestedMenus(row)) return 'info'
-    return 'primary'
-  }
-
-  const getMenuTypeText = (row) => {
-    if (row.meta?.isAuthButton || Number(row.type) === 1) return '鎸夐挳'
-    if (hasNestedMenus(row)) return '鐩綍'
-    return '鑿滃崟'
-  }
-
-  const getStatusMeta = (status) => {
-    return normalizeNumber(status, 1) === 1
-      ? { text: '鍚敤', type: 'success' }
-      : { text: '绂佺敤', type: 'danger' }
-  }
-
-  const getMenuDisplayTitle = (row) => {
-    const titleKey = row.meta?.title || row.name || ''
-    const normalizedTitleKey =
-      titleKey && !String(titleKey).includes('.') ? `menu.${titleKey}` : titleKey
-
-    return formatMenuTitle(normalizedTitleKey)
-  }
-
-  const getMenuDisplayIcon = (row) => row.meta?.icon || row.icon || ''
-
-  const { columnChecks, columns } = useTableColumns(() => [
-    {
-      prop: 'meta.title',
-      label: '鑿滃崟鍚嶇О',
-      minWidth: 180,
-      formatter: (row) => getMenuDisplayTitle(row)
-    },
-    {
-      prop: 'meta.icon',
-      label: '鍥炬爣棰勮',
-      width: 96,
-      align: 'center',
-      formatter: (row) => {
-        const icon = getMenuDisplayIcon(row)
-
-        if (!icon) return h('span', { class: 'text-g-400' }, '-')
-
-        return h(
-          'div',
-          {
-            class:
-              'mx-auto flex h-8 w-8 items-center justify-center rounded-md border border-[var(--art-border-color)] bg-[var(--art-main-bg-color)]'
-          },
-          [h(ArtSvgIcon, { icon, class: 'text-base text-g-700' })]
-        )
-      }
-    },
-    {
-      prop: 'type',
-      label: '鑿滃崟绫诲瀷',
-      width: 110,
-      formatter: (row) =>
-        h(ElTag, { type: getMenuTypeTag(row), effect: 'light' }, () => getMenuTypeText(row))
-    },
-    {
-      prop: 'route',
-      label: '璺敱',
-      minWidth: 180,
-      formatter: (row) => {
-        if (row.meta?.isAuthButton) return ''
-        return row.route || ''
-      }
-    },
-    {
-      prop: 'authority',
-      label: '鏉冮檺鏍囪瘑',
-      minWidth: 180,
-      formatter: (row) => {
-        if (row.meta?.isAuthButton) {
-          return row.authority || row.meta?.authMark || ''
-        }
-        if (!row.meta?.authList?.length) return row.authority || ''
-        return `${row.meta.authList.length} 涓潈闄愭爣璇哷
-      }
-    },
-    {
-      prop: 'sort',
-      label: '鎺掑簭',
-      width: 90
-    },
-    {
-      prop: 'status',
-      label: '鐘舵��',
-      width: 100,
-      formatter: (row) => {
-        const statusMeta = getStatusMeta(row.status)
-        return h(ElTag, { type: statusMeta.type, effect: 'light' }, () => statusMeta.text)
-      }
-    },
-    {
-      prop: 'memo',
-      label: '澶囨敞',
-      minWidth: 180,
-      showOverflowTooltip: true,
-      formatter: (row) => row.memo || '-'
-    },
-    {
-      prop: 'operation',
-      label: '鎿嶄綔',
-      width: 180,
-      align: 'right',
-      formatter: (row) => {
-        const buttonStyle = { class: 'flex justify-end' }
-        if (row.meta?.isAuthButton) {
-          return h('div', buttonStyle, [
-            h(ArtButtonTable, {
-              type: 'edit',
-              onClick: () => handleEditAuth(row)
-            }),
-            h(ArtButtonTable, {
-              type: 'delete',
-              onClick: () => handleDeleteAuth(row)
-            })
-          ])
-        }
-        return h('div', buttonStyle, [
-          h(ArtButtonTable, {
-            type: 'add',
-            onClick: () => handleAddAuth(row),
-            title: '鏂板鏉冮檺'
-          }),
-          h(ArtButtonTable, {
-            type: 'edit',
-            onClick: () => handleEditMenu(row)
-          }),
-          h(ArtButtonTable, {
-            type: 'delete',
-            onClick: () => handleDeleteMenu(row)
-          })
-        ])
-      }
-    },
-  ])
-
-  const deepClone = (obj) => {
-    if (obj === null || typeof obj !== 'object') return obj
-    if (obj instanceof Date) return new Date(obj)
-    if (Array.isArray(obj)) return obj.map((item) => deepClone(item))
-    const cloned = {}
-    for (const key in obj) {
-      if (Object.prototype.hasOwnProperty.call(obj, key)) {
-        cloned[key] = deepClone(obj[key])
-      }
-    }
-    return cloned
-  }
-
-  const convertAuthListToChildren = (items) => {
-    return items.map((item) => {
-      const clonedItem = deepClone(item)
-      if (clonedItem.children?.length) {
-        clonedItem.children = convertAuthListToChildren(clonedItem.children)
-      }
-      if (item.meta?.authList?.length) {
-        const authChildren = item.meta.authList.map((auth) => ({
-          ...deepClone(auth),
-          route: auth.route || '',
-          component: auth.component || '',
-          meta: {
-            title: auth.title,
-            authMark: auth.authMark,
-            isAuthButton: true,
-            parentPath: item.path,
-            icon: auth.icon,
-            sort: auth.sort,
-            isEnable: normalizeNumber(auth.status, 1) === 1
-          }
-        }))
-        clonedItem.children = clonedItem.children?.length
-          ? [...clonedItem.children, ...authChildren]
-          : authChildren
-      }
-      return clonedItem
+  const { columnChecks, columns } = useTableColumns(() =>
+    createMenuTableColumns({
+      titleFormatter: formatMenuTitle,
+      handleAddAuth,
+      handleEditAuth,
+      handleDeleteAuth,
+      handleEditMenu,
+      handleDeleteMenu
     })
-  }
-
-  const searchMenu = (items) => {
-    const results = []
-    for (const item of items) {
-      const searchName = appliedFilters.name?.toLowerCase().trim() || ''
-      const searchRoute = appliedFilters.route?.toLowerCase().trim() || ''
-      const menuTitle = getMenuDisplayTitle(item).toLowerCase()
-      const menuRoute = String(item.route || item.path || item.authority || '').toLowerCase()
-      const nameMatch = !searchName || menuTitle.includes(searchName)
-      const routeMatch = !searchRoute || menuRoute.includes(searchRoute)
-
-      if (item.children?.length) {
-        const matchedChildren = searchMenu(item.children)
-        if (matchedChildren.length > 0) {
-          const clonedItem = deepClone(item)
-          clonedItem.children = matchedChildren
-          results.push(clonedItem)
-          continue
-        }
-      }
-
-      if (nameMatch && routeMatch) {
-        results.push(deepClone(item))
-      }
-    }
-    return results
-  }
+  )
 
   const filteredTableData = computed(() => {
-    const searchedData = searchMenu(tableData.value)
-    return convertAuthListToChildren(searchedData)
+    const searchedData = filterMenuTree(tableData.value, appliedFilters, formatMenuTitle)
+    return expandMenuAuthChildren(searchedData)
   })
 
-  const closeDialog = () => {
+  function closeDialog() {
     dialogVisible.value = false
     editData.value = null
   }
 
-  const handleAddMenu = () => {
+  function handleAddMenu() {
     dialogType.value = 'menu'
     editData.value = null
     lockMenuType.value = true
     dialogVisible.value = true
   }
 
-  const handleAddAuth = (row) => {
+  function handleAddAuth(row) {
     dialogType.value = 'button'
     editData.value = {
       parentId: row.id,
@@ -383,37 +166,21 @@
     dialogVisible.value = true
   }
 
-  const handleEditMenu = (row) => {
+  function handleEditMenu(row) {
     dialogType.value = 'menu'
     editData.value = row
     lockMenuType.value = true
     dialogVisible.value = true
   }
 
-  const handleEditAuth = (row) => {
+  function handleEditAuth(row) {
     dialogType.value = 'button'
     editData.value = row
     lockMenuType.value = true
     dialogVisible.value = true
   }
 
-  const buildMenuSubmitPayload = (formData) => {
-    return {
-      ...(formData.id ? { id: normalizeNumber(formData.id, 0) } : {}),
-      parentId: normalizeNumber(formData.parentId, 0),
-      name: String(formData.name || '').trim(),
-      route: String(formData.route || '').trim(),
-      component: String(formData.component || '').trim(),
-      authority: String(formData.authority || '').trim(),
-      icon: String(formData.icon || '').trim(),
-      sort: normalizeNumber(formData.sort, 0),
-      status: normalizeNumber(formData.status, 1),
-      memo: String(formData.memo || '').trim(),
-      type: formData.menuType === 'button' ? 1 : 0
-    }
-  }
-
-  const handleSubmit = async (formData) => {
+  async function handleSubmit(formData) {
     const payload = buildMenuSubmitPayload(formData)
     if (payload.id && payload.id === payload.parentId) {
       ElMessage.error('涓婄骇鑿滃崟涓嶈兘閫夋嫨褰撳墠鑿滃崟')
@@ -435,10 +202,10 @@
     }
   }
 
-  const handleDeleteMenu = async (row) => {
+  async function handleDeleteMenu(row) {
     try {
       await ElMessageBox.confirm(
-        `纭畾瑕佸垹闄よ彍鍗曘��${formatMenuTitle(row.meta?.title || row.name || '')}銆嶅悧锛熷垹闄ゅ悗鏃犳硶鎭㈠`,
+        `纭畾瑕佸垹闄よ彍鍗曘��${getMenuDisplayTitle(row, formatMenuTitle)}銆嶅悧锛熷垹闄ゅ悗鏃犳硶鎭㈠`,
         '鍒犻櫎纭',
         {
           confirmButtonText: '纭畾',
@@ -456,7 +223,7 @@
     }
   }
 
-  const handleDeleteAuth = async (row) => {
+  async function handleDeleteAuth(row) {
     try {
       await ElMessageBox.confirm(`纭畾瑕佸垹闄ゆ潈闄愩��${row.name || row.authority || row.id}銆嶅悧锛熷垹闄ゅ悗鏃犳硶鎭㈠`, '鍒犻櫎纭', {
         confirmButtonText: '纭畾',
@@ -473,21 +240,21 @@
     }
   }
 
-  const handleReset = () => {
+  function handleReset() {
     Object.assign(formFilters, { ...initialSearchState })
     Object.assign(appliedFilters, { ...initialSearchState })
     loadMenuResources()
   }
 
-  const handleSearch = () => {
+  function handleSearch() {
     Object.assign(appliedFilters, { ...formFilters })
   }
 
-  const handleRefresh = () => {
+  function handleRefresh() {
     loadMenuResources()
   }
 
-  const toggleExpand = () => {
+  function toggleExpand() {
     isExpanded.value = !isExpanded.value
     nextTick(() => {
       if (tableRef.value?.elTableRef && filteredTableData.value) {

--
Gitblit v1.9.1