Files
medical-mall/layouts/admin/index.uvue
2026-01-27 20:02:57 +08:00

191 lines
5.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="layout-root">
<!-- 主侧边栏 -->
<AdminAside
:collapsed="isCollapsed"
:menuList="menuList"
:activeMenuId="activeMenuId"
@toggle="toggleCollapse"
@menu-click="onMenuClick"
/>
<!-- 二级侧边栏:固定在内容区左侧(独立层级) -->
<AdminSubSider
v-if="activeGroups.length > 0"
:activeMenuTitle="activeMenuTitle"
:groups="activeGroups"
:activeSubId="activeSubId"
@sub-click="onSubClick"
/>
<!-- 右侧内容区Header + Tags + 内容展示区 + Footer -->
<view
class="main"
:style="{ marginLeft: activeGroups.length > 0 ? '336px' : '96px' }"
>
<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>
<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 } 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('')
// 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(() => activeMenu.value?.groups || [])
const breadcrumb = computed(() => {
// “一级 / 二级”
let subTitle = ''
const groups = activeGroups.value
for (const g of groups) {
const hit = g.children.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
}
const go = (url: string) => {
// 你明确要用 navigateTo页面栈会增长这是正常行为:contentReference[oaicite:4]{index=4}
uni.navigateTo({ url })
}
const onMenuClick = (menuId: string) => {
const m = menuList.value.find(x => x.id === menuId)
if (!m) return
activeMenuId.value = m.id
// 默认激活该菜单第一个子项
const g0 = (m.groups && m.groups.length > 0) ? m.groups[0] : null
const c0 = g0 && g0.children.length > 0 ? g0.children[0] : null
activeSubId.value = c0 ? c0.id : ''
go(m.path)
}
const onSubClick = (child: any) => {
activeSubId.value = child.id
go(child.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' })
</script>
<style>
.layout-root{
width: 100%;
min-height: 100vh;
background:#f3f4f6;
}
/* 右侧主区域:左边距由 template 动态控制96 或 336 */
.main{
min-height: 100vh;
display:flex;
flex-direction: column;
}
/* 展示区 */
.content{
height: calc(100vh - 56px - 44px);
}
.content-inner{
padding: 16px;
}
</style>