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