继续完善页面布局
This commit is contained in:
@@ -1,641 +1,190 @@
|
||||
<template>
|
||||
<view class="admin-layout">
|
||||
<view class="layout-root">
|
||||
<!-- 主侧边栏 -->
|
||||
<view class="admin-sider" :class="{ 'sider-collapsed': isCollapsed }">
|
||||
<!-- Logo区域 -->
|
||||
<view class="sider-header">
|
||||
<view class="logo">
|
||||
<image class="logo-img" src="/static/logo.png" mode="aspectFit" />
|
||||
<text class="logo-text" v-if="!isCollapsed">商城后台</text>
|
||||
</view>
|
||||
<view class="collapse-btn" @click="toggleCollapse">
|
||||
<text class="iconfont">{{ isCollapsed ? 'icon-menu-unfold' : 'icon-menu-fold' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<AdminAside
|
||||
:collapsed="isCollapsed"
|
||||
:menuList="menuList"
|
||||
:activeMenuId="activeMenuId"
|
||||
@toggle="toggleCollapse"
|
||||
@menu-click="onMenuClick"
|
||||
/>
|
||||
|
||||
<!-- 一级菜单(图标 + 文案;折叠时只显示图标) -->
|
||||
<scroll-view class="menu-primary" scroll-y="true">
|
||||
<view
|
||||
v-for="menu in menuList"
|
||||
:key="menu.id"
|
||||
class="menu-item-primary"
|
||||
:class="{ active: activeMenu === menu.id }"
|
||||
@click="handleMenuClick(menu)"
|
||||
>
|
||||
<image class="menu-icon-img" :src="menu.icon" mode="aspectFit" />
|
||||
<text class="menu-title" v-if="!isCollapsed">{{ menu.title }}</text>
|
||||
<!-- 二级侧边栏:固定在内容区左侧(独立层级) -->
|
||||
<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 class="admin-main" :class="{ 'main-collapsed': isCollapsed }">
|
||||
<!-- 二级侧边栏(分组) -->
|
||||
<view class="content-sider" v-if="activeGroups.length > 0">
|
||||
<view class="content-sider-header">
|
||||
<text class="content-sider-title">{{ activeMenuTitle }}</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="content-sider-scroll" scroll-y="true">
|
||||
<view class="group-wrap">
|
||||
<view v-for="group in activeGroups" :key="group.id" class="group-block">
|
||||
<view class="group-header" @click="toggleGroup(group.id)">
|
||||
<text class="group-title">{{ group.title }}</text>
|
||||
<text class="group-arrow">{{ isGroupOpen(group.id) ? '▾' : '▸' }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="isGroupOpen(group.id)" class="group-children">
|
||||
<view
|
||||
v-for="sub in group.children"
|
||||
:key="sub.id"
|
||||
class="content-sub-menu-item"
|
||||
:class="{ active: activeSubMenu === sub.id }"
|
||||
@click="handleSubMenuClick(sub, group.id)"
|
||||
>
|
||||
<text class="content-sub-menu-text">{{ sub.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
<view class="content-area">
|
||||
<!-- 顶部 Header -->
|
||||
<view class="admin-header">
|
||||
<view class="header-left">
|
||||
<view class="breadcrumb">
|
||||
<text class="breadcrumb-item">{{ activeMenuTitle }}</text>
|
||||
<text class="breadcrumb-separator" v-if="activeSubMenuTitle"> / </text>
|
||||
<text class="breadcrumb-item active" v-if="activeSubMenuTitle">{{ activeSubMenuTitle }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="header-right">
|
||||
<view class="header-action" @click="handleSearch">
|
||||
<text class="iconfont icon-search"></text>
|
||||
</view>
|
||||
<view class="header-action" @click="handleNotification">
|
||||
<text class="iconfont icon-bell"></text>
|
||||
<view class="notification-dot" v-if="hasNotification"></view>
|
||||
</view>
|
||||
<view class="header-action" @click="handleFullscreen">
|
||||
<text class="iconfont icon-fullscreen"></text>
|
||||
</view>
|
||||
<view class="header-user" @click="handleUserMenu">
|
||||
<image class="user-avatar" src="/static/avatar/default.png" mode="aspectFill" />
|
||||
<text class="user-name">管理员</text>
|
||||
<text class="iconfont icon-down"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 页面内容区 -->
|
||||
<scroll-view class="page-content" scroll-y="true">
|
||||
<view class="page-wrapper">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
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'
|
||||
|
||||
type SubMenu = { id: string; title: string; path: string }
|
||||
type MenuGroup = { id: string; title: string; children: SubMenu[] }
|
||||
type MenuItem = { id: string; title: string; icon: string; path: string; groups?: MenuGroup[] }
|
||||
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'
|
||||
|
||||
const props = defineProps<{
|
||||
currentPage: string
|
||||
}>()
|
||||
// 你页面传进来的 currentPage:可能是顶级 id,也可能是子页面 id(user-list)
|
||||
const props = defineProps<{ currentPage: string }>()
|
||||
|
||||
const menuList = ref<MenuItem[]>(menuConst)
|
||||
|
||||
const isCollapsed = ref(false)
|
||||
const activeMenu = ref('home')
|
||||
const activeSubMenu = ref('')
|
||||
const hasNotification = ref(true)
|
||||
|
||||
// 当前激活菜单下:展开的分组
|
||||
const openGroupIds = ref<string[]>([])
|
||||
// active states
|
||||
const activeMenuId = ref('home')
|
||||
const activeSubId = ref('')
|
||||
|
||||
// ✅ 你 static 里 6 个 svg:homepage / user / shopping / order / statistics / setting
|
||||
// 菜单多于 6 个时允许复用(财务、统计都用 statistics.svg)
|
||||
const menuList = ref<MenuItem[]>([
|
||||
{
|
||||
id: 'home',
|
||||
title: '首页',
|
||||
icon: '/static/homepage.svg',
|
||||
path: '/pages/mall/admin/index',
|
||||
groups: []
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
title: '用户',
|
||||
icon: '/static/user.svg',
|
||||
path: '/pages/mall/admin/user-management',
|
||||
groups: [
|
||||
{
|
||||
id: 'user-group',
|
||||
title: '用户管理',
|
||||
children: [
|
||||
{ id: 'user-list', title: '用户列表', path: '/pages/mall/admin/user-management' },
|
||||
{ id: 'user-add', title: '添加用户', path: '/pages/mall/admin/user-management?action=add' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'product',
|
||||
title: '商品',
|
||||
icon: '/static/shopping.svg',
|
||||
path: '/pages/mall/admin/product-management',
|
||||
groups: [
|
||||
{
|
||||
id: 'product-group',
|
||||
title: '商品管理',
|
||||
children: [
|
||||
{ id: 'product-list', title: '商品列表', path: '/pages/mall/admin/product-management' },
|
||||
{ id: 'product-add', title: '添加商品', path: '/pages/mall/admin/product-management?action=add' },
|
||||
{ id: 'category', title: '商品分类', path: '/pages/mall/admin/product-management?tab=category' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: '订单',
|
||||
icon: '/static/order.svg',
|
||||
path: '/pages/mall/admin/order-management',
|
||||
groups: [
|
||||
{
|
||||
id: 'order-group',
|
||||
title: '订单管理',
|
||||
children: [
|
||||
{ id: 'order-list', title: '订单列表', path: '/pages/mall/admin/order-management' },
|
||||
{ id: 'order-detail', title: '订单详情', path: '/pages/mall/admin/order-management?action=detail' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'finance',
|
||||
title: '财务',
|
||||
icon: '/static/finance.svg',
|
||||
path: '/pages/mall/admin/finance-management',
|
||||
groups: [
|
||||
{
|
||||
id: 'finance-group',
|
||||
title: '财务管理',
|
||||
children: [
|
||||
{ id: 'finance-overview', title: '财务概览', path: '/pages/mall/admin/finance-management' },
|
||||
{ id: 'withdrawals', title: '提现管理', path: '/pages/mall/admin/finance-management?tab=withdrawals' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'statistics',
|
||||
title: '统计',
|
||||
icon: '/static/statistics.svg',
|
||||
path: '/pages/mall/admin/user-statistics',
|
||||
groups: []
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
title: '设置',
|
||||
icon: '/static/setting.svg',
|
||||
path: '/pages/mall/admin/system-settings',
|
||||
groups: [
|
||||
{
|
||||
id: 'system-group',
|
||||
title: '系统设置',
|
||||
children: [
|
||||
{ id: 'basic', title: '基本设置', path: '/pages/mall/admin/system-settings' },
|
||||
{ id: 'security', title: '安全设置', path: '/pages/mall/admin/system-settings?tab=security' },
|
||||
{ id: 'email', title: '邮件设置', path: '/pages/mall/admin/system-settings?tab=email' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
// tabs
|
||||
const tabs = ref<TabItem[]>([
|
||||
{ id: 'home', title: '首页', path: '/pages/mall/admin/homePage/index' }
|
||||
])
|
||||
const activeTabId = ref('home')
|
||||
|
||||
/** ✅ 关键:把 currentPage(可能是子菜单 id)反推到顶级菜单 + 子菜单 */
|
||||
const resolveActiveByPageId = (pageId: string) => {
|
||||
// 1) 命中顶级
|
||||
const top = menuList.value.find(m => m.id === pageId)
|
||||
if (top) {
|
||||
const first = getFirstChild(top)
|
||||
return { menuId: top.id, subId: first ? first.id : '', groupId: first ? first.groupId : '' }
|
||||
}
|
||||
|
||||
// 2) 命中二级:groups.children
|
||||
for (let i = 0; i < menuList.value.length; i++) {
|
||||
const m = menuList.value[i]
|
||||
const gs = m.groups || []
|
||||
for (let g = 0; g < gs.length; g++) {
|
||||
const group = gs[g]
|
||||
for (let c = 0; c < group.children.length; c++) {
|
||||
const child = group.children[c]
|
||||
if (child.id === pageId) {
|
||||
return { menuId: m.id, subId: child.id, groupId: group.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 找不到:回退到首页
|
||||
return { menuId: 'home', subId: '', groupId: '' }
|
||||
// 每次 layout 渲染时,同步高亮(靠 currentPage)
|
||||
const syncActiveByCurrentPage = () => {
|
||||
const r = findActiveByCurrentPage(menuList.value, props.currentPage)
|
||||
activeMenuId.value = r.activeMenuId
|
||||
activeSubId.value = r.activeSubId
|
||||
}
|
||||
|
||||
const getFirstChild = (menu: MenuItem) => {
|
||||
const gs = menu.groups || []
|
||||
if (gs.length === 0) return null
|
||||
if (gs[0].children.length === 0) return null
|
||||
return { ...gs[0].children[0], groupId: gs[0].id }
|
||||
// 同步 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
|
||||
}
|
||||
|
||||
const setOpenGroupsForMenu = (menuId: string, forceOpenGroupId: string) => {
|
||||
const m = menuList.value.find(x => x.id === menuId)
|
||||
const gs = (m && m.groups) ? m.groups : []
|
||||
// 默认全展开;如果传了 forceOpenGroupId,确保它在列表里
|
||||
const ids = gs.map(g => g.id)
|
||||
openGroupIds.value = ids
|
||||
if (forceOpenGroupId && ids.indexOf(forceOpenGroupId) < 0) {
|
||||
openGroupIds.value.push(forceOpenGroupId)
|
||||
// 初始化同步(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 }
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentPage,
|
||||
(val) => {
|
||||
const r = resolveActiveByPageId(val || 'home')
|
||||
activeMenu.value = r.menuId
|
||||
activeSubMenu.value = r.subId
|
||||
setOpenGroupsForMenu(r.menuId, r.groupId)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const activeMenuTitle = computed(() => {
|
||||
const m = menuList.value.find(x => x.id === activeMenu.value)
|
||||
return m ? m.title : '后台'
|
||||
})
|
||||
|
||||
const activeGroups = computed(() => {
|
||||
const m = menuList.value.find(x => x.id === activeMenu.value)
|
||||
return (m && m.groups) ? m.groups : []
|
||||
})
|
||||
|
||||
const activeSubMenuTitle = computed(() => {
|
||||
const gs = activeGroups.value
|
||||
for (let g = 0; g < gs.length; g++) {
|
||||
const group = gs[g]
|
||||
const sub = group.children.find(x => x.id === activeSubMenu.value)
|
||||
if (sub) return sub.title
|
||||
}
|
||||
return ''
|
||||
return subTitle ? `${activeMenuTitle.value} / ${subTitle}` : `${activeMenuTitle.value}`
|
||||
})
|
||||
|
||||
// handlers
|
||||
const toggleCollapse = () => {
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
}
|
||||
|
||||
const isGroupOpen = (groupId: string) => {
|
||||
return openGroupIds.value.indexOf(groupId) >= 0
|
||||
}
|
||||
|
||||
const toggleGroup = (groupId: string) => {
|
||||
const idx = openGroupIds.value.indexOf(groupId)
|
||||
if (idx >= 0) openGroupIds.value.splice(idx, 1)
|
||||
else openGroupIds.value.push(groupId)
|
||||
}
|
||||
|
||||
// ✅ 后台切页建议用 redirectTo,避免页面栈越堆越深;目标页必须在 pages.json 注册 :contentReference[oaicite:2]{index=2}
|
||||
const go = (url: string) => {
|
||||
uni.redirectTo({ url })
|
||||
// 你明确要用 navigateTo:页面栈会增长(这是正常行为):contentReference[oaicite:4]{index=4}
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const handleMenuClick = (menu: MenuItem) => {
|
||||
activeMenu.value = menu.id
|
||||
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 first = getFirstChild(menu)
|
||||
if (first) {
|
||||
activeSubMenu.value = first.id
|
||||
setOpenGroupsForMenu(menu.id, first.groupId || '')
|
||||
go(first.path)
|
||||
} else {
|
||||
activeSubMenu.value = ''
|
||||
setOpenGroupsForMenu(menu.id, '')
|
||||
go(menu.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 handleSubMenuClick = (subMenu: SubMenu, groupId: string) => {
|
||||
activeSubMenu.value = subMenu.id
|
||||
if (!isGroupOpen(groupId)) openGroupIds.value.push(groupId)
|
||||
go(subMenu.path)
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
uni.showToast({ title: '搜索功能', icon: 'none' })
|
||||
}
|
||||
const handleNotification = () => {
|
||||
uni.showToast({ title: '通知中心', icon: 'none' })
|
||||
}
|
||||
const handleFullscreen = () => {
|
||||
uni.showToast({ title: '全屏切换', icon: 'none' })
|
||||
}
|
||||
const handleUserMenu = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['个人信息', '退出登录'],
|
||||
success: (res) => {
|
||||
uni.showToast({ title: res.tapIndex === 0 ? '个人信息' : '退出登录', icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
const onSearch = () => uni.showToast({ title: '搜索', icon: 'none' })
|
||||
const onRefresh = () => uni.showToast({ title: '刷新', icon: 'none' })
|
||||
const onNotify = () => uni.showToast({ title: '通知', icon: 'none' })
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
background-color: #f0f2f5;
|
||||
.layout-root{
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background:#f3f4f6;
|
||||
}
|
||||
|
||||
/* ===== 主侧边栏 ===== */
|
||||
.admin-sider {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 220px;
|
||||
background-color: #001529;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: width 0.25s ease;
|
||||
}
|
||||
.admin-sider.sider-collapsed {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.sider-header {
|
||||
height: 64px;
|
||||
padding: 0 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #002140;
|
||||
border-bottom: 1px solid #002140;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.logo-img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.logo-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.collapse-btn:active {
|
||||
background-color: rgba(255,255,255,0.12);
|
||||
}
|
||||
|
||||
.menu-primary {
|
||||
flex: 1;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.menu-item-primary {
|
||||
margin: 8px 10px;
|
||||
padding: 10px 8px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: rgba(255,255,255,0.80);
|
||||
}
|
||||
.menu-item-primary.active {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
.menu-item-primary:active {
|
||||
background-color: rgba(24,144,255,0.65);
|
||||
}
|
||||
|
||||
.menu-icon-img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
||||
}
|
||||
.menu-title {
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* ===== 主内容区:二级侧边栏 + 内容区(横向布局,避免二级菜单跑到顶部)===== */
|
||||
.admin-main {
|
||||
margin-left: 220px;
|
||||
width: calc(100% - 220px);
|
||||
height: 100vh;
|
||||
display: flex; /* 关键:横向 */
|
||||
flex-direction: row; /* 关键:横向 */
|
||||
transition: margin-left 0.25s ease, width 0.25s ease;
|
||||
}
|
||||
.admin-main.main-collapsed {
|
||||
margin-left: 80px;
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
|
||||
/* ===== 二级侧边栏(分组导航)===== */
|
||||
.content-sider {
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
display: flex;
|
||||
/* 右侧主区域:左边距由 template 动态控制(96 或 336) */
|
||||
.main{
|
||||
min-height: 100vh;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.content-sider-header {
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.content-sider-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
.content-sider-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.group-wrap {
|
||||
padding: 10px 0;
|
||||
/* 展示区 */
|
||||
.content{
|
||||
height: calc(100vh - 56px - 44px);
|
||||
}
|
||||
|
||||
.group-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 10px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #595959;
|
||||
}
|
||||
.group-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.group-arrow {
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.group-children {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.content-sub-menu-item {
|
||||
height: 38px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #595959;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.content-sub-menu-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.content-sub-menu-item.active {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
border-left-color: #1890ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.content-sub-menu-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* ===== 右侧内容区 ===== */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
min-width: 0; /* 防止横向溢出 */
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
/* 顶部 Header */
|
||||
.admin-header {
|
||||
height: 64px;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 18px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
.breadcrumb {
|
||||
font-size: 15px;
|
||||
color: #666666;
|
||||
}
|
||||
.breadcrumb-item {
|
||||
color: #1890ff;
|
||||
}
|
||||
.breadcrumb-item.active {
|
||||
color: #262626;
|
||||
}
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.header-action {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
}
|
||||
.header-action:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.notification-dot {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #ff4d4f;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
.header-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.header-user:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.user-avatar {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-name {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 页面内容区 */
|
||||
.page-content {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
.page-wrapper {
|
||||
padding: 18px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* iconfont(保留你现有的 header/折叠按钮图标) */
|
||||
.iconfont {
|
||||
font-family: 'iconfont';
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
.content-inner{
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user