修复bug

This commit is contained in:
2026-01-30 19:00:31 +08:00
parent fcc976680d
commit 3de5e9ebe9
91 changed files with 4966 additions and 847 deletions

View File

@@ -1,254 +1,5 @@
<template>
<view class="layout-root">
<!-- 主侧边栏 -->
<AdminAside
:collapsed="isCollapsed"
:menuList="menuList"
:activeMenuId="activeMenuId"
@toggle="toggleCollapse"
@menu-click="onMenuClick"
:asideWidth='ASIDE_W'
/>
<!-- 二级侧边栏:固定在内容区左侧(独立层级) -->
<AdminSubSider
v-if="activeGroups.length > 0"
:activeMenuTitle="activeMenuTitle"
:groups="activeGroups"
:activeSubId="activeSubId"
:activeMenuId="activeMenuId || 'home'"
:asideWidth="ASIDE_W"
:siderWidth="SUB_W"
@sub-click="onSubClick"
/>
<!-- 右侧内容区Header + Tags + 内容展示区 + Footer -->
<view
class="main"
:style="{ marginLeft: mainLeft }"
>
<AdminHeader
:breadcrumb="breadcrumb"
:hasNotification="hasNotification"
@search="onSearch"
@refresh="onRefresh"
@notify="onNotify"
/>
<AdminTagsView
:tabs="tabs"
:activeTabId="activeTabId"
@tab-click="onTabClick"
@tab-close="onTabClose"
/>
<!-- 展示区:只渲染 slot 内容(你的页面内容都在这里展示) -->
<scroll-view class="content" scroll-y="true">
<view class="content-inner">
<slot></slot>
</view>
<AdminFooter />
</scroll-view>
</view>
</view>
</template>
<template><view /></template>
<script setup lang="uts">
import { ref, computed } from 'vue'
import AdminAside from './components/AdminAside.uvue'
import AdminSubSider from './components/AdminSubSider.uvue'
import AdminHeader from './components/AdminHeader.uvue'
import AdminTagsView from './components/AdminTagsView.uvue'
import AdminFooter from './components/AdminFooter.uvue'
import { menuList as menuConst } from './utils/menu.uts'
import { findActiveByCurrentPage, getCurrentRoutePath } from './utils/nav.uts'
import { makeTabFromPath, upsertTab, removeTab } from './utils/tabs.uts'
import type { MenuItem, TabItem , MenuChild } from './types.uts'
// 你页面传进来的 currentPage可能是顶级 id也可能是子页面 iduser-list
const props = defineProps<{ currentPage: string }>()
const menuList = ref<MenuItem[]>(menuConst)
const isCollapsed = ref(false)
const hasNotification = ref(true)
// active states
const activeMenuId = ref('home')
const activeSubId = ref('')
// 二级侧边栏
const ASIDE_W = 96
const SUB_W = 200 // 你想更像 CRMEB就把这改小160~180 都行
const mainLeft = computed(() => {
return (activeGroups.value.length > 0 ? (ASIDE_W + SUB_W) : ASIDE_W) + 'px'
})
// tabs
const tabs = ref<TabItem[]>([
{ id: 'home', title: '首页', path: '/pages/mall/admin/homePage/index' }
])
const activeTabId = ref('home')
// 每次 layout 渲染时,同步高亮(靠 currentPage
const syncActiveByCurrentPage = () => {
const r = findActiveByCurrentPage(menuList.value, props.currentPage)
activeMenuId.value = r.activeMenuId
activeSubId.value = r.activeSubId
}
// 同步 tabs靠当前 route
const syncTabsByRoute = () => {
const path = getCurrentRoutePath()
if (!path) return
const tab = makeTabFromPath(menuList.value, path)
tabs.value = upsertTab(tabs.value, tab)
activeTabId.value = tab.id
}
// 初始化同步setup 执行一次)
syncActiveByCurrentPage()
syncTabsByRoute()
// computed
const activeMenu = computed(() => menuList.value.find(m => m.id === activeMenuId.value))
const activeMenuTitle = computed(() => activeMenu.value?.title || '商城后台')
const activeGroups = computed(() => {
const m = menuList.value.find(it => it.id === activeMenuId.value)
return m?.groups ?? [] // ✅ 永远是数组
})
const breadcrumb = computed(() => {
let subTitle = ''
const groups = activeGroups.value
for (const g of groups) {
const cs = g.children ?? []
const hit = cs.find(c => c.id === activeSubId.value)
if (hit) { subTitle = hit.title; break }
}
return subTitle ? `${activeMenuTitle.value} / ${subTitle}` : `${activeMenuTitle.value}`
})
// handlers
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value
}
// 递归取第一个 leaf你要求的“递归默认打开第一个页面”
const firstLeafOfMenu = (m: MenuItem): MenuChild | null => {
if (!m.groups || m.groups.length === 0) return null
const g0 = m.groups[0]
if (!g0.children || g0.children.length === 0) return null
const c0: any = g0.children[0] as any
// 兼容未来 children 还能再嵌套
if (c0.children && (c0.children as MenuChild[]).length > 0) {
const walk = (list: MenuChild[]): MenuChild | null => {
if (!list || list.length === 0) return null
const n: any = list[0] as any
if (n.children && (n.children as MenuChild[]).length > 0) return walk(n.children as MenuChild[])
return list[0]
}
return walk(c0.children as MenuChild[])
}
return g0.children[0]
}
let navigating = false
const go = async (url?: string | null) => {
if (!url || url.length === 0) return
if (navigating) return
navigating = true
try {
await uni.navigateTo({ url })
} catch (e) {
// 可选:忽略取消类报错,避免控制台刷屏
} finally {
setTimeout(() => { navigating = false }, 80)
}
}
const onMenuClick = (menuId: string) => {
const m = menuList.value.find(x => x.id === menuId)
if (!m) return
activeMenuId.value = m.id
const leaf = firstLeafOfMenu(m)
activeSubId.value = leaf ? leaf.id : ''
// ✅ 优先 leaf.path其次才考虑 m.path
go(leaf?.path ?? m.path ?? '')
}
const firstLeafInChildren = (list?: MenuChild[] | null): MenuChild | null => {
const arr = list ?? []
if (arr.length === 0) return null
const n = arr[0]
const deep = n.children ?? []
return deep.length > 0 ? firstLeafInChildren(deep) : n
}
const onSubClick = (c: MenuChild) => {
activeSubId.value = c.id
if (c.path && c.path.length > 0) {
go(c.path)
} else {
const leaf = firstLeafInChildren(c.children)
go(leaf?.path ?? '')
}
}
const onTabClick = (tab: TabItem) => {
activeTabId.value = tab.id
go(tab.path)
}
const onTabClose = (tabId: string) => {
// 关闭当前 tab删除后回到最后一个 tab
const wasActive = activeTabId.value === tabId
tabs.value = removeTab(tabs.value, tabId)
if (wasActive) {
const last = tabs.value[tabs.value.length - 1]
if (last) {
activeTabId.value = last.id
go(last.path)
}
}
}
const onSearch = () => uni.showToast({ title: '搜索', icon: 'none' })
const onRefresh = () => uni.showToast({ title: '刷新', icon: 'none' })
const onNotify = () => uni.showToast({ title: '通知', icon: 'none' })
uni.redirectTo({ url: '/pages/mall/admin/homePage/index' })
</script>
<style>
.layout-root{
width: 100%;
min-height: 100vh;
background:#f3f4f6;
}
/* 右侧主区域:左边距由 template 动态控制96 或 336 */
.main{
min-height: 100vh;
display:flex;
flex-direction: rowe;
}
/* 展示区 */
.content{
height: calc(100vh - 56px - 44px);
}
.content-inner{
padding:5px;
}
</style>