2026-07-14 10:31:17 +08:00
|
|
|
|
import { computed, defineComponent, ref } from 'vue';
|
2026-07-17 08:51:48 +08:00
|
|
|
|
import { Layout, Menu, Breadcrumb, Modal, Avatar, Space } from 'ant-design-vue';
|
|
|
|
|
|
import { RouterView, useRouter } from 'vue-router';
|
2026-07-14 10:31:17 +08:00
|
|
|
|
import {
|
|
|
|
|
|
MenuFoldOutlined,
|
|
|
|
|
|
MenuUnfoldOutlined,
|
2026-07-17 08:51:48 +08:00
|
|
|
|
ReloadOutlined,
|
|
|
|
|
|
SearchOutlined,
|
|
|
|
|
|
BellOutlined,
|
|
|
|
|
|
FullscreenOutlined,
|
|
|
|
|
|
UserOutlined,
|
2026-07-14 10:31:17 +08:00
|
|
|
|
} from '@ant-design/icons-vue';
|
2026-07-17 08:51:48 +08:00
|
|
|
|
import { useBasicLayout } from '../hooks/useBasicLayout';
|
|
|
|
|
|
import { auth } from '@/hooks/useAuth';
|
2026-07-14 10:31:17 +08:00
|
|
|
|
import styles from './BasicLayout.module.less';
|
|
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
// 解构 Layout 组件
|
|
|
|
|
|
const { Header, Sider, Content } = Layout;
|
2026-07-14 10:31:17 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-07-17 08:51:48 +08:00
|
|
|
|
* 基础布局组件
|
|
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* CPMS 运营平台的主布局结构,包含:
|
|
|
|
|
|
* - 左侧侧边栏:Logo + 导航菜单
|
|
|
|
|
|
* - 顶部 Header:菜单折叠按钮 + 面包屑 + 用户信息
|
|
|
|
|
|
* - 内容区域:路由视图渲染区域
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 在路由中使用
|
|
|
|
|
|
* {
|
|
|
|
|
|
* path: '/',
|
|
|
|
|
|
* component: BasicLayout,
|
|
|
|
|
|
* children: [...]
|
|
|
|
|
|
* }
|
|
|
|
|
|
* ```
|
2026-07-14 10:31:17 +08:00
|
|
|
|
*/
|
|
|
|
|
|
export default defineComponent({
|
|
|
|
|
|
name: 'BasicLayout',
|
|
|
|
|
|
setup() {
|
2026-07-17 08:51:48 +08:00
|
|
|
|
// 使用布局 Hook 获取菜单和面包屑数据
|
|
|
|
|
|
const { breadcrumbItems, menuItems, getActiveMenuKey, getOpenKeys, handleMenuClick } =
|
|
|
|
|
|
useBasicLayout();
|
2026-07-14 10:31:17 +08:00
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
// 路由实例,用于退出登录后跳转到登录页
|
|
|
|
|
|
const router = useRouter();
|
2026-07-14 10:31:17 +08:00
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 侧边栏折叠状态
|
|
|
|
|
|
* true: 折叠状态(只显示图标)
|
|
|
|
|
|
* false: 展开状态(显示图标+文字)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const collapsed = ref(false);
|
2026-07-14 10:31:17 +08:00
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 切换侧边栏折叠/展开状态
|
|
|
|
|
|
*/
|
2026-07-14 10:31:17 +08:00
|
|
|
|
const toggleCollapsed = () => {
|
|
|
|
|
|
collapsed.value = !collapsed.value;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 退出登录:二次确认后跳转到登录页(不调用接口)
|
|
|
|
|
|
* - 点击用户名弹出确认弹框
|
|
|
|
|
|
* - 确定 -> 跳转登录页
|
|
|
|
|
|
* - 取消 -> 关闭弹框,不做任何处理
|
|
|
|
|
|
*/
|
|
|
|
|
|
const handleLogout = () => {
|
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
|
title: '确认退出',
|
|
|
|
|
|
content: '确定要退出当前账号吗?',
|
|
|
|
|
|
okText: '确定',
|
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
|
onOk: () => {
|
|
|
|
|
|
// 清除本地登录态(不调用接口),否则路由守卫会把 /login 重定向回首页
|
|
|
|
|
|
auth.logout();
|
|
|
|
|
|
router.replace('/login');
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-14 10:31:17 +08:00
|
|
|
|
return () => (
|
|
|
|
|
|
<Layout class={styles.container}>
|
2026-07-17 08:51:48 +08:00
|
|
|
|
{/* 左侧侧边栏 */}
|
|
|
|
|
|
<Sider
|
|
|
|
|
|
class={styles.sider}
|
|
|
|
|
|
collapsed={collapsed.value}
|
|
|
|
|
|
trigger={null}
|
|
|
|
|
|
collapsible
|
|
|
|
|
|
width={220}
|
|
|
|
|
|
>
|
|
|
|
|
|
{/* Logo 区域 */}
|
2026-07-14 10:31:17 +08:00
|
|
|
|
<div class={styles.logo}>
|
2026-07-17 08:51:48 +08:00
|
|
|
|
<div class={styles.logoIcon} />
|
|
|
|
|
|
<span class={styles.logoText}>{collapsed.value ? 'CPMS' : 'CPMS运营平台'}</span>
|
2026-07-14 10:31:17 +08:00
|
|
|
|
</div>
|
2026-07-17 08:51:48 +08:00
|
|
|
|
|
|
|
|
|
|
{/* 导航菜单 */}
|
2026-07-14 10:31:17 +08:00
|
|
|
|
<Menu
|
2026-07-17 08:51:48 +08:00
|
|
|
|
theme="light"
|
2026-07-14 10:31:17 +08:00
|
|
|
|
mode="inline"
|
2026-07-17 08:51:48 +08:00
|
|
|
|
selectedKeys={[getActiveMenuKey()]}
|
|
|
|
|
|
openKeys={getOpenKeys()}
|
2026-07-14 10:31:17 +08:00
|
|
|
|
items={menuItems}
|
|
|
|
|
|
onClick={handleMenuClick}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Sider>
|
|
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
{/* 右侧内容区域 */}
|
2026-07-14 10:31:17 +08:00
|
|
|
|
<Layout>
|
2026-07-17 08:51:48 +08:00
|
|
|
|
{/* 顶部 Header */}
|
|
|
|
|
|
<Header class={styles.header} style={{ background: '#ffffff' }}>
|
|
|
|
|
|
<div class={styles.headerLeft}>
|
|
|
|
|
|
{/* 折叠/展开按钮 */}
|
|
|
|
|
|
<span class={styles.trigger} onClick={toggleCollapsed}>
|
|
|
|
|
|
{collapsed.value ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 刷新按钮 */}
|
|
|
|
|
|
{/* <span class={styles.refreshIcon}>
|
|
|
|
|
|
<ReloadOutlined />
|
|
|
|
|
|
</span> */}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 面包屑导航 */}
|
|
|
|
|
|
{breadcrumbItems.value.length > 0 && (
|
|
|
|
|
|
<Breadcrumb class={styles.breadcrumb}>
|
|
|
|
|
|
{breadcrumbItems.value.map((item) => (
|
|
|
|
|
|
<Breadcrumb.Item key={item.key}>{item.title}</Breadcrumb.Item>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Breadcrumb>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Header 右侧:用户信息 */}
|
|
|
|
|
|
<div class={styles.headerRight}>
|
|
|
|
|
|
<Space size="middle">
|
|
|
|
|
|
{/* 搜索图标 */}
|
|
|
|
|
|
{/* <span class={styles.headerIcon}>
|
|
|
|
|
|
<SearchOutlined />
|
|
|
|
|
|
</span> */}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 通知图标 */}
|
|
|
|
|
|
{/* <span class={styles.headerIcon}>
|
|
|
|
|
|
<BellOutlined />
|
|
|
|
|
|
</span> */}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 全屏图标 */}
|
|
|
|
|
|
{/* <span class={styles.headerIcon}>
|
|
|
|
|
|
<FullscreenOutlined />
|
|
|
|
|
|
</span> */}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 用户信息:点击弹出退出登录确认 */}
|
|
|
|
|
|
<div class={styles.userInfo} onClick={handleLogout}>
|
|
|
|
|
|
<Avatar size="small" icon={<UserOutlined />} />
|
|
|
|
|
|
<span class={styles.userName}>用户名</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
2026-07-14 10:31:17 +08:00
|
|
|
|
</Header>
|
|
|
|
|
|
|
2026-07-17 08:51:48 +08:00
|
|
|
|
{/* 内容区域 */}
|
2026-07-14 10:31:17 +08:00
|
|
|
|
<Content class={styles.content}>
|
2026-07-17 08:51:48 +08:00
|
|
|
|
<div class={styles.pageContent}>
|
|
|
|
|
|
{/* 路由视图:根据当前路由渲染对应页面 */}
|
|
|
|
|
|
<RouterView />
|
|
|
|
|
|
</div>
|
2026-07-14 10:31:17 +08:00
|
|
|
|
</Content>
|
|
|
|
|
|
</Layout>
|
|
|
|
|
|
</Layout>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|