主页示意图
用户在菜单区点击菜单后,菜单对应路由内容将显示在右侧主操作区,同时添加新的标签页(如果标签页不存在),用户点击不同的页签完成页面内容切换。这种页面分区效果是大部分后台管理系统使用的页面风格。下面我们就一步步来完成页面框架的开发吧。
1、创建布局组件
在src\views目录中添加Layout.vue文件,文件内容如下:
<script setup>
</script>
<template>
<a-layout>
<a-layout-header><span style="color: white;">Header</span></a-layout-header>
<a-layout>
<a-layout-sider><span style="color: white;">Sider</span></a-layout-sider>
<a-layout-content>Content</a-layout-content>
</a-layout>
</a-layout>
</template>
<style scoped>
section.ant-layout {
height: 100%;
}
</style>
根据标签名称基本可以看出每个标签所对应含义。样式section.ant-layout是为了让整个页面高度占满屏幕可视区域。
2、修改App.vue文件
修改App.vue文件内容,引入Layout.vue组件并定义组件渲染位置:
<script setup>
import {ref} from 'vue';
import layout from "./views/Layout.vue";//引入组件,名称和template中的标签名相同即可
const pageMaskShow = ref(false);
const showPageMask = () => {
pageMaskShow = true;
};
const closePageMask = () => {
pageMaskShow = false;
};
</script>
<template>
<a-spin size="large" :spinning="pageMaskShow"><!--使用加载组件的目的是为后续ajax请求时添加遮罩-->
<layout></layout>
</a-spin>
</template>
<style scoped>
.ant-spin-nested-loading {/*遮罩高度100%*/
height: 100%;
}
:deep(.ant-spin-container) {/*遮罩容器高度100%*/
height: 100%;
}
</style>
这里需要注意:由于相关组件在dom结构中都属于id为app的div的子元素,因此需要将id为app的div高度设置为100%,否则后续子元素高度100%将不会生效。要设置id为app的div的高度,需要在index.html文件中添加<style>标签,index.html文件内容如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
<style>
#app {/*添加样式定义*/
height: 100%;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
3、查看效果
在vscode终端执行npm run dev命令启动服务,打开浏览器访问http://localhost:8090
,出现以下页面
首页:框架页面
4、定义系统菜单组件
在src\views文件夹中创建SysMenu.vue文件,文件内容如下:
<script setup>
import { ref } from 'vue';
import { PieChartOutlined, MailOutlined } from '@ant-design/icons-vue';
const SubMenu = {
name: 'SubMenu',
props: {
menuInfo: {
type: Object,
default: () => ({}),
},
},
template: `
<a-sub-menu :key="menuInfo.key">
<template #icon><MailOutlined /></template>
<template #title>{{ menuInfo.title }}</template>
<template v-for="item in menuInfo.children" :key="item.key">
<template v-if="!item.children">
<a-menu-item :key="item.key">
<template #icon>
<PieChartOutlined />
</template>
{{ item.title }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.key" />
</template>
</template>
</a-sub-menu>
`,
components: {
PieChartOutlined,
MailOutlined,
},
};
const list = [{
key: '1',
title: '页面1',
}, {
key: '2',
title: '页面2',
}];
const selectedKeys = ref([]);//处于选中状态的节点key列表
const openKeys = ref([]);//处于展开状态的节点key列表
function selectedMenuNode(nodeInfo) { //根据选择的菜单,路由到不同的页面
if (nodeInfo.key == '1') {
$router.push("/page1");
} else if (nodeInfo.key == '2') {
$router.push("/page2");
}
}
</script>
<template>
<a-menu v-model:openKeys="openKeys" v-model:selectedKeys="selectedKeys" mode="inline" theme="dark" @select="selectedMenuNode">
<template v-for="item in list" :key="item.key">
<template v-if="!item.children">
<a-menu-item :key="item.key">
<template #icon>
<PieChartOutlined />
</template>
{{ item.title }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :key="item.key" :menu-info="item" />
</template>
</template>
</a-menu>
</template>
<style scoped>
</style>
菜单由list数组数据生成,这个菜单数据可以与后端服务联动,通过数据库表定义菜单数据,不过这是后话。
注意:如果ant-design-vue3的单页递归生成菜单组件如果出现运行异常,需要修改vite.config.js文件,修改后的内容如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'vue': 'vue/dist/vue.esm-bundler.js' // 定义vue的别名,如果使用其他的插件,可能会用到别名,该配置同时解决ant-design-vue中单页递归生成导航菜单时会出现异常的问题
}
},
plugins: [vue()],
server: {
port: 8090,//前端端口
hmr: true,//是否启用热i部署
proxy: {//反向代理,通过axios调用后端服务,解决跨域问题
"/api": {
target: "http://localhost:8081",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
}
}
})
5、修改Layout.vue文件
文件内容如下:
<script setup>
import {ref} from 'vue';
import sysmenu from './SysMenu.vue';//引入菜单组件
const isCollapsed = ref(false);
const switchCollapsed = () => {
isCollapsed.value = !isCollapsed.value;
};
</script>
<template>
<a-layout>
<a-layout-header><span style="color: white;">Header</span></a-layout-header>
<a-layout>
<a-layout-sider v-model:collapsed="isCollapsed">
<sysmenu></sysmenu><!--自定义标签,与引入的组件变量名一致-->
</a-layout-sider>
<a-layout-content>
<router-view></router-view><!--路由显示区域-->
</a-layout-content>
</a-layout>
</a-layout>
</template>
<style scoped>
section.ant-layout {
height: 100%;
}
</style>
这样,点击菜单后,就可以在主操作区显示不同的页面内容了。
现在,应用页面框架基本形成,只是现在只能做到页面覆盖,还做不到多标签页切换,下一篇文章将改造相关功能,达到多页面切换的效果。