mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-05 19:41:57 +08:00
feat: layout支持side和top两种布局模式,两种模式支持fixedHeader和fixedSideBar
This commit is contained in:
parent
4eb8257f43
commit
a5bfd009d3
@ -3,7 +3,6 @@
|
||||
"version": "2.0.0-alpha.0",
|
||||
"description": "@webank/fes-compiler",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
|
@ -6,7 +6,6 @@
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"module": "dist/index.esm.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
@ -9,7 +9,6 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"module": "dist/index.esm.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||
|
@ -6,7 +6,6 @@
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"module": "dist/index.esm.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ const Layout = defineComponent({
|
||||
return null;
|
||||
},
|
||||
};
|
||||
return <BaseLayout {...userConfig} v-slots={slots}></BaseLayout>;
|
||||
return <BaseLayout locale={ localeShared ? true : false } {...userConfig} v-slots={slots}></BaseLayout>;
|
||||
};
|
||||
}
|
||||
})
|
||||
|
@ -1,49 +1,75 @@
|
||||
<template>
|
||||
<a-layout class="main-layout">
|
||||
<a-layout-sider
|
||||
v-if="routeHasLayout"
|
||||
v-model:collapsed="collapsed"
|
||||
:width="sideWidth"
|
||||
:class="{ collapsed: collapsed }"
|
||||
collapsible
|
||||
theme="dark"
|
||||
class="layout-sider"
|
||||
>
|
||||
<div class="logo">
|
||||
<img :src="logo" class="logo-img" />
|
||||
<h1 class="logo-name">{{title}}</h1>
|
||||
</div>
|
||||
<Menu :menus="menus" :theme="theme" />
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<a-layout-header v-if="routeHasLayout" class="layout-header">
|
||||
<div class="layout-header-user">
|
||||
<a-layout
|
||||
v-if="routeHasLayout"
|
||||
:class="[
|
||||
collapsed ? 'main-layout-collapsed' : '',
|
||||
`main-layout-navigation-${navigation}`
|
||||
]"
|
||||
class="main-layout"
|
||||
>
|
||||
<template v-if="navigation !== 'top'">
|
||||
<div v-if="fixedSideBar" class="layout-sider-fixed-stuff"></div>
|
||||
<a-layout-sider
|
||||
v-model:collapsed="collapsed"
|
||||
:width="sideWidth"
|
||||
:class="[
|
||||
'layout-sider',
|
||||
fixedSideBar ? 'layout-sider-fixed' : ''
|
||||
]"
|
||||
collapsible
|
||||
theme="dark"
|
||||
>
|
||||
<div class="layout-logo">
|
||||
<img :src="logo" class="logo-img" />
|
||||
<h1 class="logo-name">{{title}}</h1>
|
||||
</div>
|
||||
<Menu :menus="menus" :theme="theme" />
|
||||
</a-layout-sider>
|
||||
</template>
|
||||
<a-layout class="child-layout">
|
||||
<a-layout-header v-if="fixedHeader" class="layout-header">
|
||||
</a-layout-header>
|
||||
<a-layout-header
|
||||
:class="[fixedHeader ? 'layout-header-fixed' : '']"
|
||||
class="layout-header"
|
||||
>
|
||||
<template v-if="navigation === 'top'">
|
||||
<div class="layout-logo">
|
||||
<img :src="logo" class="logo-img" />
|
||||
<h1 class="logo-name">{{title}}</h1>
|
||||
</div>
|
||||
<Menu :menus="menus" :theme="theme" class="layout-menu" mode="horizontal" />
|
||||
</template>
|
||||
<div class="layout-header-custom">
|
||||
<slot name="userCenter"></slot>
|
||||
</div>
|
||||
<slot name="locale"></slot>
|
||||
<template v-if="locale">
|
||||
<slot name="locale"></slot>
|
||||
</template>
|
||||
</a-layout-header>
|
||||
<a-layout-content class="layout-content">
|
||||
<MultiTabProvider v-if="multiTabs" />
|
||||
<router-view v-else></router-view>
|
||||
</a-layout-content>
|
||||
<a-layout-footer v-if="routeHasLayout" class="layout-footer">
|
||||
<a-layout-footer class="layout-footer">
|
||||
Ant Design ©2020 Created by MumbleFe
|
||||
</a-layout-footer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
<div v-else class="layout-content">
|
||||
<MultiTabProvider v-if="multiTabs" />
|
||||
<router-view v-else></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ref, computed
|
||||
} from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from '@@/core/coreExports';
|
||||
import Layout from 'ant-design-vue/lib/layout';
|
||||
import 'ant-design-vue/lib/layout/style';
|
||||
import Menu from './Menu';
|
||||
import MultiTabProvider from './MultiTabProvider';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
[Layout.name]: Layout,
|
||||
@ -75,7 +101,7 @@ export default {
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'dark'
|
||||
default: 'dark' // light、dark
|
||||
},
|
||||
navigation: {
|
||||
type: String,
|
||||
@ -113,29 +139,86 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
<style lang="less" vars="{ sideWidth: sideWidth +'px' }">
|
||||
.main-layout {
|
||||
min-height: 100vh;
|
||||
.layout-sider{
|
||||
&.collapsed{
|
||||
.logo{
|
||||
&.main-layout-collapsed {
|
||||
.layout-sider {
|
||||
.logo {
|
||||
justify-content: center;
|
||||
.logo-name{
|
||||
.logo-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
.layout-sider-fixed-stuff {
|
||||
overflow: hidden;
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
&.main-layout-navigation-top {
|
||||
.layout-header {
|
||||
padding-left: 24px;
|
||||
color: hsla(0,0%,100%,.65);
|
||||
background: #001529;
|
||||
.layout-menu {
|
||||
line-height: 48px;
|
||||
}
|
||||
.layout-logo {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
min-width: 165px;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: all .3s;
|
||||
.logo-img {
|
||||
height: 32px;
|
||||
width: auto;
|
||||
}
|
||||
.logo-name {
|
||||
overflow: hidden;
|
||||
margin: 0 0 0 12px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
&.layout-header-fixed {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.layout-sider-fixed-stuff {
|
||||
overflow: hidden;
|
||||
width: var(--sideWidth);
|
||||
transition: width 0.2s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.child-layout {
|
||||
position: relative;
|
||||
}
|
||||
.layout-sider {
|
||||
&.layout-sider-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
}
|
||||
.layout-logo {
|
||||
height: 32px;
|
||||
margin: 16px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
.logo-img{
|
||||
.logo-img {
|
||||
height: 32px;
|
||||
width: auto;
|
||||
}
|
||||
.logo-name{
|
||||
.logo-name {
|
||||
overflow: hidden;
|
||||
margin: 0 0 0 12px;
|
||||
color: #fff;
|
||||
@ -153,10 +236,18 @@ export default {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
padding: 0 24px;
|
||||
.layout-header-user {
|
||||
flex: 1
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
padding: 0;
|
||||
.layout-header-custom {
|
||||
flex: 1;
|
||||
}
|
||||
&.layout-header-fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: var(--sideWidth);
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
width: calc(100% - var(--sideWidth));
|
||||
}
|
||||
}
|
||||
.layout-content {
|
||||
|
@ -1,11 +1,38 @@
|
||||
<template>
|
||||
<a-tabs :activeKey="route.path" @tabClick="switchTab" class="layout-content-tabs" hide-add type="editable-card">
|
||||
<a-tab-pane v-for="page in openedPageList" :key="page.path" closable>
|
||||
<a-tabs
|
||||
:activeKey="route.path"
|
||||
@tabClick="switchPage"
|
||||
class="layout-content-tabs"
|
||||
hide-add
|
||||
type="editable-card"
|
||||
>
|
||||
<a-tab-pane v-for="page in pageList" :key="page.path" closable>
|
||||
<template #tab>
|
||||
{{page.name}}
|
||||
<ReloadOutlined v-show="route.path === page.path" @click="reloadTab(page.path)" class="layout-tabs-close-icon" />
|
||||
<ReloadOutlined
|
||||
v-show="route.path === page.path"
|
||||
@click="reloadPage(page.path)"
|
||||
class="layout-tabs-close-icon"
|
||||
/>
|
||||
</template>
|
||||
</a-tab-pane>
|
||||
<template #tabBarExtraContent>
|
||||
<a-dropdown>
|
||||
<div class="layout-tabs-more-icon">
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu @click="handlerMore">
|
||||
<a-menu-item key="closeOtherPage">
|
||||
<a href="javascript:;">关闭其他</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="reloadPage">
|
||||
<a href="javascript:;">刷新当前页</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-tabs>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<keep-alive>
|
||||
@ -14,38 +41,46 @@
|
||||
</router-view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
reactive, unref
|
||||
} from 'vue';
|
||||
import { reactive, unref } from 'vue';
|
||||
import Tabs from 'ant-design-vue/lib/tabs';
|
||||
import Dropdown from 'ant-design-vue/lib/dropdown';
|
||||
import Menu from 'ant-design-vue/lib/menu';
|
||||
import 'ant-design-vue/lib/menu/style';
|
||||
import 'ant-design-vue/lib/dropdown/style';
|
||||
import 'ant-design-vue/lib/tabs/style';
|
||||
import { ReloadOutlined } from '@ant-design/icons-vue';
|
||||
import { ReloadOutlined, MoreOutlined } from '@ant-design/icons-vue';
|
||||
import { useRouter, useRoute } from '@@/core/coreExports';
|
||||
|
||||
let i = 0;
|
||||
const getKey = () => ++i;
|
||||
export default {
|
||||
components: {
|
||||
[Dropdown.name]: Dropdown,
|
||||
[Menu.name]: Menu,
|
||||
[Menu.Item.name]: Menu.Item,
|
||||
[Tabs.name]: Tabs,
|
||||
[Tabs.TabPane.name]: Tabs.TabPane,
|
||||
ReloadOutlined
|
||||
ReloadOutlined,
|
||||
MoreOutlined
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const openedPageList = reactive([{
|
||||
path: unref(route.path),
|
||||
route: {
|
||||
query: unref(route.query),
|
||||
params: unref(route.params)
|
||||
},
|
||||
name: unref(route.meta).name,
|
||||
key: getKey()
|
||||
}]);
|
||||
const findPage = path => openedPageList.find(item => unref(item.path) === path);
|
||||
const pageList = reactive([
|
||||
{
|
||||
path: unref(route.path),
|
||||
route: {
|
||||
query: unref(route.query),
|
||||
params: unref(route.params)
|
||||
},
|
||||
name: unref(route.meta).name,
|
||||
key: getKey()
|
||||
}
|
||||
]);
|
||||
const findPage = path => pageList.find(item => unref(item.path) === unref(path));
|
||||
router.beforeEach((to) => {
|
||||
if (!findPage(to.path)) {
|
||||
openedPageList.push({
|
||||
pageList.push({
|
||||
path: to.path,
|
||||
route: to,
|
||||
name: to.meta.name,
|
||||
@ -55,7 +90,7 @@ export default {
|
||||
return true;
|
||||
});
|
||||
// 还需要考虑参数
|
||||
const switchTab = (path) => {
|
||||
const switchPage = (path) => {
|
||||
const selectedPage = findPage(path);
|
||||
if (selectedPage) {
|
||||
router.push({
|
||||
@ -65,12 +100,17 @@ export default {
|
||||
});
|
||||
}
|
||||
};
|
||||
const reloadTab = (path) => {
|
||||
const selectedPage = findPage(path);
|
||||
const reloadPage = (path) => {
|
||||
const selectedPage = findPage(path || unref(route.path));
|
||||
if (selectedPage) {
|
||||
selectedPage.key = getKey();
|
||||
}
|
||||
};
|
||||
const closeOtherPage = (path) => {
|
||||
const selectedPage = findPage(path || unref(route.path));
|
||||
pageList.length = 0;
|
||||
pageList.push(selectedPage);
|
||||
};
|
||||
const getPageKey = (_route) => {
|
||||
const selectedPage = findPage(_route.path);
|
||||
if (selectedPage) {
|
||||
@ -78,12 +118,25 @@ export default {
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const handlerMore = ({ key }) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case 'closeOtherPage':
|
||||
closeOtherPage();
|
||||
break;
|
||||
case 'reloadPage':
|
||||
reloadPage();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
return {
|
||||
route,
|
||||
openedPageList,
|
||||
pageList,
|
||||
getPageKey,
|
||||
reloadTab,
|
||||
switchTab
|
||||
reloadPage,
|
||||
switchPage,
|
||||
handlerMore
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -96,15 +149,23 @@ export default {
|
||||
width: 100%;
|
||||
.ant-tabs-nav-container {
|
||||
padding-left: 16px;
|
||||
.layout-tabs-close-icon {
|
||||
vertical-align: middle;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 12px;
|
||||
margin-left: 6px;
|
||||
margin-top: -2px;
|
||||
&:hover{
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
.layout-tabs-close-icon {
|
||||
vertical-align: middle;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 12px;
|
||||
margin-left: 6px;
|
||||
margin-top: -2px;
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
.layout-tabs-more-icon {
|
||||
margin-right: 8px;
|
||||
padding: 0 4px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"module": "dist/index.esm.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-dropdown>
|
||||
<GlobalOutlined />
|
||||
<div class="lang-icon"><GlobalOutlined /></div>
|
||||
<template #overlay>
|
||||
<a-menu :selectedKeys="selectedKeys" @click="handleClick">
|
||||
<a-menu-item
|
||||
@ -53,6 +53,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.lang-icon {
|
||||
margin: 0 8px;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lang-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -6,7 +6,6 @@
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"module": "dist/index.esm.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
@ -9,7 +9,6 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"module": "dist/index.esm.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/WeBankFinTech/fes.js.git",
|
||||
|
Loading…
x
Reference in New Issue
Block a user