Files
cpms_operation_platform/src/layouts/BasicLayout.tsx
T

108 lines
2.9 KiB
TypeScript
Raw Normal View History

2026-07-23 11:27:33 +08:00
import { computed, defineComponent, h, ref } from 'vue';
2026-07-14 10:31:17 +08:00
import { useRouter, useRoute, RouterView } from 'vue-router';
import { Layout, Menu } from 'ant-design-vue';
import type { MenuProps } from 'ant-design-vue';
import {
DashboardOutlined,
InfoCircleOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
2026-07-23 11:27:33 +08:00
SettingOutlined,
2026-07-14 10:31:17 +08:00
} from '@ant-design/icons-vue';
2026-07-23 11:27:33 +08:00
import { useMenuStore } from '@/stores/menuStore';
2026-07-14 10:31:17 +08:00
import styles from './BasicLayout.module.less';
const { Header, Sider, Content, Footer } = Layout;
/**
2026-07-23 11:27:33 +08:00
* 图标名 → 组件映射
* 后端下发的 icon 字符串在此映射为 antd icon 组件
* 扩展新图标只需在此添加一行
*/
const ICON_MAP: Record<string, any> = {
DashboardOutlined,
InfoCircleOutlined,
SettingOutlined,
};
/**
* 将 menuStore 中的 menuItems 转为 antd Menu 的 items 格式
* 递归处理 iconName → icon VNode
*/
function resolveMenuItems(items: any[]): MenuProps['items'] {
return items.map((item) => {
const resolved: any = {
key: item.key,
label: item.label,
};
// iconName 字符串转为 VNode
if (item.iconName && ICON_MAP[item.iconName]) {
resolved.icon = () => h(ICON_MAP[item.iconName]);
}
if (item.children?.length) {
resolved.children = resolveMenuItems(item.children);
}
return resolved;
});
}
/**
* 基础布局:左侧导航(后端菜单驱动) + 顶部栏 + 内容区 + 页脚
2026-07-14 10:31:17 +08:00
*/
export default defineComponent({
name: 'BasicLayout',
setup() {
const route = useRoute();
const router = useRouter();
const collapsed = ref(false);
2026-07-23 11:27:33 +08:00
const { menuItems } = useMenuStore();
2026-07-14 10:31:17 +08:00
const selectedKeys = computed<string[]>(() => [route.path]);
2026-07-23 11:27:33 +08:00
const resolvedItems = computed(() => resolveMenuItems(menuItems.value));
2026-07-14 10:31:17 +08:00
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
router.push(key as string);
};
const toggleCollapsed = () => {
collapsed.value = !collapsed.value;
};
return () => (
<Layout class={styles.container}>
<Sider class={styles.sider} collapsed={collapsed.value} trigger={null} collapsible>
<div class={styles.logo}>
<span class={styles.logoText}>{collapsed.value ? 'CPMS' : 'CPMS 运营平台'}</span>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={selectedKeys.value}
2026-07-23 11:27:33 +08:00
items={resolvedItems.value}
2026-07-14 10:31:17 +08:00
onClick={handleMenuClick}
/>
</Sider>
<Layout>
<Header class={styles.header}>
<span class={styles.trigger} onClick={toggleCollapsed}>
{collapsed.value ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</span>
</Header>
<Content class={styles.content}>
<RouterView />
</Content>
<Footer class={styles.footer}>CPMS 运营平台 ©{new Date().getFullYear()}</Footer>
</Layout>
</Layout>
);
},
});