diff --git a/package-lock.json b/package-lock.json
index f571eb63..f714bd41 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "fes.js",
-  "version": "0.1.0",
+  "version": "2.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/packages/fes-plugin-layout/README.md b/packages/fes-plugin-layout/README.md
new file mode 100644
index 00000000..46ae75dd
--- /dev/null
+++ b/packages/fes-plugin-layout/README.md
@@ -0,0 +1,5 @@
+主题: light/dark
+布局: 左右(上/下)、上/下、上/下(左/右)
+固定Header: 是/否
+固定sidebar: 是/否
+multi tabs: 是/否
diff --git a/packages/fes-plugin-layout/package.json b/packages/fes-plugin-layout/package.json
index 88da06ce..bdb34f5a 100644
--- a/packages/fes-plugin-layout/package.json
+++ b/packages/fes-plugin-layout/package.json
@@ -15,6 +15,7 @@
   "license": "MIT",
   "peerDependencies": {
     "vue": "^3.0.0",
-    "@webank/fes": "^2.0.0"
+    "@webank/fes": "^2.0.0",
+    "ant-design-vue": "2.0.0-rc.3"
   }
 }
diff --git a/packages/fes-plugin-layout/src/helpers.js b/packages/fes-plugin-layout/src/helpers.js
deleted file mode 100644
index b9743ef1..00000000
--- a/packages/fes-plugin-layout/src/helpers.js
+++ /dev/null
@@ -1 +0,0 @@
-export const noop = () => { };
diff --git a/packages/fes-plugin-layout/src/helpers/addAccessTag.js b/packages/fes-plugin-layout/src/helpers/addAccessTag.js
new file mode 100644
index 00000000..feedb371
--- /dev/null
+++ b/packages/fes-plugin-layout/src/helpers/addAccessTag.js
@@ -0,0 +1,35 @@
+import { unref, computed } from 'vue';
+import { useAccess } from '@webank/fes';
+
+if (!useAccess) {
+    throw new Error(
+        '[plugin-layout]: pLugin-layout依赖plugin-access,请先安装plugin-access!'
+    );
+}
+
+const hasAccess = (item) => {
+    let res;
+    if (item.path && (!item.children || item.children.length === 0)) {
+        res = useAccess(item.path);
+    } else if (item.children && item.children.length > 0) {
+        res = computed(() => item.children.some(child => hasAccess(child)));
+    }
+    return res;
+};
+
+const addAcessTag = (arr) => {
+    if (Array.isArray(arr)) {
+        arr.forEach((item) => {
+            item.access = hasAccess(item);
+            if (item.children && item.children.length > 0) {
+                addAcessTag(item.children);
+            }
+        });
+    }
+};
+
+export default function (menus) {
+    const originData = unref(menus);
+    addAcessTag(originData);
+    return originData;
+}
diff --git a/packages/fes-plugin-layout/src/helpers/index.js b/packages/fes-plugin-layout/src/helpers/index.js
new file mode 100644
index 00000000..484d4343
--- /dev/null
+++ b/packages/fes-plugin-layout/src/helpers/index.js
@@ -0,0 +1,65 @@
+export const noop = () => {};
+
+const matchName = (config, name) => {
+    let res;
+    if (Array.isArray(config)) {
+        for (let i = 0; i < config.length; i++) {
+            const item = config[i];
+            if (item.meta && item.meta.name === name) {
+                res = item.meta;
+                res.path = item.path;
+                break;
+            }
+            if (item.children && item.children.length > 0) {
+                res = matchName(item.children, name);
+                if (res) {
+                    break;
+                }
+            }
+        }
+    }
+    return res;
+};
+
+const matchPath = (config, path) => {
+    let res = {};
+    if (Array.isArray(config)) {
+        for (let i = 0; i < config.length; i++) {
+            const item = config[i];
+            if (item.path && item.path === path) {
+                res = item.meta;
+                res.path = item.path;
+                break;
+            }
+            if (item.children && item.children.length > 0) {
+                res = matchPath(item.children, path);
+                if (res) {
+                    break;
+                }
+            }
+        }
+    }
+    return res;
+};
+
+export const fillMenuData = (menuConfig, routeConfig, dep = 0) => {
+    dep += 1;
+    if (dep > 3) {
+        throw new Error('[plugin-layout]: menu层级不能超出三层!');
+    }
+    const arr = [];
+    if (Array.isArray(menuConfig) && Array.isArray(routeConfig)) {
+        menuConfig.forEach((item) => {
+            if (item.path !== undefined && item.path !== null) {
+                Object.assign(item, matchPath(routeConfig, item.path));
+            } else {
+                Object.assign(item, matchName(routeConfig, item.name));
+            }
+            if (item.children && item.children.length > 0) {
+                item.children = fillMenuData(item.children, routeConfig, dep);
+            }
+            arr.push(item);
+        });
+    }
+    return arr;
+};
diff --git a/packages/fes-plugin-layout/src/index.js b/packages/fes-plugin-layout/src/index.js
index 71a8174b..1e7eda2b 100644
--- a/packages/fes-plugin-layout/src/index.js
+++ b/packages/fes-plugin-layout/src/index.js
@@ -1,4 +1,4 @@
-import { readFileSync } from 'fs';
+import { readFileSync, copyFileSync, statSync } from 'fs';
 import { join } from 'path';
 
 const namespace = 'plugin-layout';
@@ -9,53 +9,55 @@ export default (api) => {
     } = api;
 
     api.describe({
+        key: 'layout',
         config: {
             schema(joi) {
-                return joi.object({
-                    menus: joi.array()
-                });
+                return joi.object();
             },
-            default: {}
+            onChange: api.ConfigChangeType.regenerateTmpFiles
         }
     });
 
-    const absoluteFilePath = join(namespace, 'core.js');
-
     const absRuntimeFilePath = join(namespace, 'runtime.js');
 
     api.onGenerateFiles(() => {
         // 文件写出
-        const { menus = [] } = api.config.layout || {};
-
-        console.log(menus);
-
-        // api.writeTmpFile({
-        //     path: absoluteFilePath,
-        //     content: Mustache.render(
-        //         readFileSync(join(__dirname, 'template/core.tpl'), 'utf-8'),
-        //         {
-        //             REPLACE_ROLES: JSON.stringify(roles)
-        //         }
-        //     )
-        // });
+        const userConfig = api.config.layout || {};
 
         api.writeTmpFile({
             path: absRuntimeFilePath,
-            content: readFileSync(
-                join(__dirname, 'template/runtime.tpl'),
-                'utf-8'
+            content: Mustache.render(
+                readFileSync(join(__dirname, 'template/runtime.tpl'), 'utf-8'),
+                {
+                    REPLACE_USER_CONFIG: JSON.stringify(userConfig)
+                }
             )
         });
     });
 
-    // api.addPluginExports(() => [
-    //     {
-    //         specifiers: ['access', 'useAccess'],
-    //         source: absoluteFilePath
-    //     }
-    // ]);
+    let generatedOnce = false;
+    api.onGenerateFiles(() => {
+        if (generatedOnce) return;
+        generatedOnce = true;
+        const cwd = join(__dirname, '../src');
+        const files = api.utils.glob.sync('**/*', {
+            cwd
+        });
+        const base = join(api.paths.absTmpPath, namespace);
+        files.forEach((file) => {
+            if (file.indexOf('template') !== -1) return;
+            if (file === 'index.js') return;
+            const source = join(cwd, file);
+            const target = join(base, file);
+            if (statSync(source).isDirectory()) {
+                api.utils.mkdirp.sync(target);
+            } else {
+                copyFileSync(source, target);
+            }
+        });
+    });
 
-    // api.addRuntimePluginKey(() => 'noAccessHandler');
+    api.addRuntimePluginKey(() => 'layout');
 
-    // api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
+    api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
 };
diff --git a/packages/fes-plugin-layout/src/template/runtime.tpl b/packages/fes-plugin-layout/src/template/runtime.tpl
index 7fc09b78..34817e1c 100644
--- a/packages/fes-plugin-layout/src/template/runtime.tpl
+++ b/packages/fes-plugin-layout/src/template/runtime.tpl
@@ -1,23 +1,30 @@
+import { reactive, toRefs } from "vue";
+import { getRoutes, plugin, ApplyPluginsType } from "@@/core/coreExports";
+import BaseLayout from "./views/BaseLayout.vue";
+import { fillMenuData } from "./helpers";
+
+const userConfig = reactive({{{REPLACE_USER_CONFIG}}});
 export function rootContainer(childComponent, args) {
-    const useRuntimeConfig =
-        plugin.applyPlugins({
-            key: "initialStateConfig",
-            type: ApplyPluginsType.modify,
-            initialValue: {},
-        }) || {};
-    return {
-        setup() {
-            const { loading } = useModel("@@initialState") || {};
-            return () => {
-                if (loading.value) {
-                    return useRuntimeConfig.loading ? (
-                        <useRuntimeConfig.loading />
-                    ) : (
-                        <></>
-                    );
+    const runtimeConfig = plugin.applyPlugins({
+        key: "layout",
+        type: ApplyPluginsType.modify,
+        initialValue: {},
+    });
+    const routeConfig = getRoutes();
+    userConfig.menus = fillMenuData(userConfig.menus, routeConfig);
+    return () => {
+        const slots = {
+            default: () => <childComponent></childComponent>,
+            userCenter: () => {
+                if(runtimeConfig.userCenter){
+                    return <runtimeConfig.userCenter></runtimeConfig.userCenter>
                 }
-                return <childComponent />;
-            };
-        },
+                return <></>
+            }
+          };
+        return (
+            <BaseLayout {...userConfig} v-slots={slots}>
+            </BaseLayout>
+        );
     };
 }
\ No newline at end of file
diff --git a/packages/fes-plugin-layout/src/views/BaseLayout.vue b/packages/fes-plugin-layout/src/views/BaseLayout.vue
index e69de29b..1bf08293 100644
--- a/packages/fes-plugin-layout/src/views/BaseLayout.vue
+++ b/packages/fes-plugin-layout/src/views/BaseLayout.vue
@@ -0,0 +1,151 @@
+<template>
+    <a-layout class="main-layout">
+        <a-layout-sider
+            v-if="routeLayout"
+            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="routeLayout" class="layout-header">
+                <slot name="userCenter"></slot>
+            </a-layout-header>
+            <a-layout-content class="layout-content">
+                <slot></slot>
+            </a-layout-content>
+            <a-layout-footer v-if="routeLayout" class="layout-footer">
+                Ant Design ©2020 Created by MumbleFe
+            </a-layout-footer>
+        </a-layout>
+    </a-layout>
+</template>
+
+<script>
+import { ref, computed } from 'vue';
+import { useRoute } from '@webank/fes';
+import Layout from 'ant-design-vue/lib/layout';
+import 'ant-design-vue/lib/layout/style';
+import Menu from './Menu.vue';
+
+export default {
+    components: {
+        [Layout.name]: Layout,
+        [Layout.Sider.name]: Layout.Sider,
+        [Layout.Content.name]: Layout.Content,
+        [Layout.Header.name]: Layout.Header,
+        [Layout.Footer.name]: Layout.Footer,
+        Menu
+    },
+    props: {
+        menus: {
+            type: Array,
+            default() {
+                return [];
+            }
+        },
+        title: {
+            type: String,
+            default: ''
+        },
+        locale: {
+            type: Boolean,
+            default: false
+        },
+        logo: {
+            type: String,
+            default: ''
+        },
+        theme: {
+            type: String,
+            default: 'dark'
+        },
+        navigation: {
+            type: String,
+            default: 'side' // side 左右(上/下)、 top 上/下、 mixin 上/下(左/右)
+        },
+        fixedHeader: {
+            type: Boolean,
+            default: false
+        },
+        fixedSideBar: {
+            type: Boolean,
+            default: true
+        },
+        multiTabs: {
+            type: Boolean,
+            default: false
+        },
+        sideWidth: {
+            type: Number,
+            default: 200
+        }
+    },
+    setup(props, content) {
+        const route = useRoute();
+        const routeLayout = computed(() => {
+            const _routeLayout = route.meta.layout;
+            return _routeLayout === undefined ? true : _routeLayout;
+        });
+        return {
+            routeLayout,
+            collapsed: ref(false)
+        };
+    }
+};
+</script>
+
+<style lang="less">
+.main-layout {
+    min-height: 100vh;
+    .layout-sider{
+        &.collapsed{
+            .logo{
+                justify-content: center;
+                .logo-name{
+                    display: none;
+                }
+            }
+        }
+        .logo {
+            height: 32px;
+            margin: 16px;
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
+            .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 {
+        height: 48px;
+        line-height: 48px;
+        background: #fff;
+        padding: 0;
+    }
+    .layout-content {
+        position: relative;
+    }
+    .layout-footer {
+        text-align: center;
+    }
+}
+</style>
diff --git a/packages/fes-plugin-layout/src/views/Menu.vue b/packages/fes-plugin-layout/src/views/Menu.vue
new file mode 100644
index 00000000..00053c21
--- /dev/null
+++ b/packages/fes-plugin-layout/src/views/Menu.vue
@@ -0,0 +1,104 @@
+<template>
+    <a-menu
+        :selectedKeys="selectedKeys"
+        @click="onMenuClick"
+        theme="dark"
+        mode="inline"
+    >
+        <template v-for="(item, index) in menus" :key="index">
+            <template v-if="item.access">
+                <a-sub-menu v-if="item.children" :title="item.title">
+                    <template
+                        v-for="(item1, index) in item.children"
+                        :key="index"
+                    >
+                        <template v-if="item1.access">
+                            <a-sub-menu
+                                v-if="item1.children"
+                                :title="item1.title"
+                            >
+                                <template
+                                    v-for="(item2, index) in item1.children"
+                                    :key="index"
+                                >
+                                    <a-menu-item
+                                        v-if="item2.access"
+                                        :key="item2.path"
+                                    >
+                                        {{item2.title}}
+                                    </a-menu-item>
+                                </template>
+                            </a-sub-menu>
+                            <a-menu-item v-else :key="item1.path">
+                                {{item1.title}}
+                            </a-menu-item>
+                        </template>
+                    </template>
+                </a-sub-menu>
+                <a-menu-item v-else :key="item.path">
+                    <UserOutlined />
+                    <span>{{item.title}}</span>
+                </a-menu-item>
+            </template>
+        </template>
+    </a-menu>
+</template>
+
+<script>
+import { ref, toRefs, computed } from 'vue';
+import { useRoute, useRouter } from '@webank/fes';
+import Menu from 'ant-design-vue/lib/menu';
+import 'ant-design-vue/lib/menu/style';
+import {
+    UserOutlined
+} from '@ant-design/icons-vue';
+import addAccessTag from '../helpers/addAccessTag';
+
+export default {
+    components: {
+        [Menu.name]: Menu,
+        [Menu.SubMenu.name]: Menu.SubMenu,
+        [Menu.Item.name]: Menu.Item,
+        UserOutlined
+    },
+    props: {
+        menus: {
+            type: Array,
+            default() {
+                return [];
+            }
+        },
+        theme: {
+            type: String,
+            default: 'dark'
+        }
+    },
+    setup(props) {
+        const { menus } = toRefs(props);
+        const route = useRoute();
+        const router = useRouter();
+        const fixedMenus = addAccessTag(menus);
+        const onMenuClick = (e) => {
+            const path = e.key;
+            if (/^https?:\/\//.test(path)) {
+                window.open(path, '_blank');
+            } else if (/^\//.test(path)) {
+                router.push(path);
+            } else {
+                console.warn(
+                    '[plugin-layout]: 菜单的path只能使以http(s)开头的网址或者路由地址'
+                );
+            }
+        };
+        const selectedKeys = computed(() => [route.path]);
+        return {
+            selectedKeys,
+            menus: fixedMenus,
+            onMenuClick
+        };
+    }
+};
+</script>
+
+<style lang="less">
+</style>
diff --git a/packages/fes-plugin-layout/src/views/layout.vue b/packages/fes-plugin-layout/src/views/layout.vue
deleted file mode 100644
index d31e3806..00000000
--- a/packages/fes-plugin-layout/src/views/layout.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<template>
-    <a-layout id="components-layout-demo-custom-trigger">
-        <a-layout-sider
-            v-model:collapsed="collapsed"
-            :trigger="null"
-            collapsible
-        >
-            <div class="logo" />
-            <a-menu
-                v-model:selectedKeys="selectedKeys"
-                theme="dark"
-                mode="inline"
-            >
-                <a-menu-item key="1">
-                    <user-outlined />
-                    <span>nav 1</span>
-                </a-menu-item>
-                <a-menu-item key="2">
-                    <video-camera-outlined />
-                    <span>nav 2</span>
-                </a-menu-item>
-                <a-menu-item key="3">
-                    <upload-outlined />
-                    <span>nav 3</span>
-                </a-menu-item>
-            </a-menu>
-        </a-layout-sider>
-        <a-layout>
-            <a-layout-header style="background: #fff; padding: 0">
-                <menu-unfold-outlined
-                    v-if="collapsed"
-                    @click="() => (collapsed = !collapsed)"
-                    class="trigger"
-                />
-                <menu-fold-outlined
-                    v-else
-                    @click="() => (collapsed = !collapsed)"
-                    class="trigger"
-                />
-            </a-layout-header>
-            <a-layout-content
-                :style="{
-                    margin: '24px 16px',
-                    padding: '24px',
-                    background: '#fff',
-                    minHeight: '280px'
-                }"
-            >
-                Content
-            </a-layout-content>
-        </a-layout>
-    </a-layout>
-</template>
-
-<script>
-import {
-    UserOutlined,
-    VideoCameraOutlined,
-    UploadOutlined,
-    MenuUnfoldOutlined,
-    MenuFoldOutlined
-} from '@ant-design/icons-vue';
-
-export default {
-    components: {
-        UserOutlined,
-        VideoCameraOutlined,
-        UploadOutlined,
-        MenuUnfoldOutlined,
-        MenuFoldOutlined
-    },
-    data() {
-        return {
-            selectedKeys: ['1'],
-            collapsed: false
-        };
-    }
-};
-</script>
-
-<style>
-#components-layout-demo-custom-trigger .trigger {
-    font-size: 18px;
-    line-height: 64px;
-    padding: 0 24px;
-    cursor: pointer;
-    transition: color 0.3s;
-}
-
-#components-layout-demo-custom-trigger .trigger:hover {
-    color: #1890ff;
-}
-
-#components-layout-demo-custom-trigger .logo {
-    height: 32px;
-    background: rgba(255, 255, 255, 0.2);
-    margin: 16px;
-}
-</style>
diff --git a/packages/fes-preset-built-in/src/plugins/commands/dev/index.js b/packages/fes-preset-built-in/src/plugins/commands/dev/index.js
index ad6151dd..3c03fbcf 100644
--- a/packages/fes-preset-built-in/src/plugins/commands/dev/index.js
+++ b/packages/fes-preset-built-in/src/plugins/commands/dev/index.js
@@ -33,7 +33,6 @@ export default (api) => {
         description: 'start a dev server for development',
         async fn({ args = {} }) {
             const defaultPort = process.env.PORT || args.port || api.config.devServer?.port;
-            console.log(api.config.devServer);
             port = await portfinder.getPortPromise({
                 port: defaultPort ? parseInt(String(defaultPort), 10) : 8000
             });
diff --git a/packages/fes-preset-built-in/src/plugins/misc/route/index.js b/packages/fes-preset-built-in/src/plugins/misc/route/index.js
index 2afb5d61..2855b2c1 100644
--- a/packages/fes-preset-built-in/src/plugins/misc/route/index.js
+++ b/packages/fes-preset-built-in/src/plugins/misc/route/index.js
@@ -267,5 +267,12 @@ export default function (api) {
         });
     });
 
+    api.addCoreExports(() => [
+        {
+            specifiers: ['getRoutes'],
+            source: absCoreFilePath
+        }
+    ]);
+
     api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
 }
diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js
index 3afe65a6..8d38133d 100644
--- a/packages/fes-template/.fes.js
+++ b/packages/fes-template/.fes.js
@@ -1,15 +1,19 @@
-// fes.config.js 只负责管理 cli 相关的配置
+// .fes.js 只负责管理编译时配置,只能使用plain Object
 
 
 export default {
     access: {
         roles: {
-            admin: ["/"]
+            admin: ["/", "/onepiece"]
         }
     },
     layout: {
+        title: "Fes.js",
+        logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
         menus: [{
-            path: '/'
+            name: 'index'
+        }, {
+            name: 'onepiece'
         }]
     },
     devServer: {
diff --git a/packages/fes-template/src/app.js b/packages/fes-template/src/app.js
index 70e65954..e837a94a 100644
--- a/packages/fes-template/src/app.js
+++ b/packages/fes-template/src/app.js
@@ -1,6 +1,6 @@
 import { access } from '@webank/fes';
 import PageLoading from '@/components/PageLoading.vue';
-
+import UserCenter from '@/components/UserCenter.vue';
 
 export const beforeRender = {
     loading: <PageLoading />,
@@ -10,10 +10,13 @@ export const beforeRender = {
             setTimeout(() => {
                 setRole('admin');
                 resolve({
-                    a: 1,
-                    b: 2
+                    userName: 'harrywan'
                 });
-            }, 3000);
+            }, 1000);
         });
     }
 };
+
+export const layout = {
+    userCenter: <UserCenter />
+};
diff --git a/packages/fes-template/src/components/PageLoading.vue b/packages/fes-template/src/components/PageLoading.vue
index bc678fd3..88c79423 100644
--- a/packages/fes-template/src/components/PageLoading.vue
+++ b/packages/fes-template/src/components/PageLoading.vue
@@ -4,7 +4,7 @@
     </div>
 </template>
 <script>
-import { Spin } from 'ant-design-vue';
+import Spin from 'ant-design-vue/lib/spin';
 import 'ant-design-vue/lib/spin/style';
 
 export default {
diff --git a/packages/fes-template/src/components/UserCenter.vue b/packages/fes-template/src/components/UserCenter.vue
new file mode 100644
index 00000000..e0066039
--- /dev/null
+++ b/packages/fes-template/src/components/UserCenter.vue
@@ -0,0 +1,15 @@
+<template>
+    <div>{{initialState.userName}}</div>
+</template>
+<script>
+import { useModel } from '@webank/fes';
+
+export default {
+    setup() {
+        const { initialState } = useModel('@@initialState');
+        return {
+            initialState
+        };
+    }
+};
+</script>
diff --git a/packages/fes-template/src/pages/index.vue b/packages/fes-template/src/pages/index.vue
index 418cdbd3..d96cf094 100644
--- a/packages/fes-template/src/pages/index.vue
+++ b/packages/fes-template/src/pages/index.vue
@@ -6,8 +6,9 @@
 </template>
 <config>
 {
+    "name": "index",
     "title": "首页",
-    "layout": "false"
+    "layout": false
 }
 </config>
 <script>
diff --git a/packages/fes-template/src/pages/onepiece.vue b/packages/fes-template/src/pages/onepiece.vue
index 3df0594e..063dea96 100644
--- a/packages/fes-template/src/pages/onepiece.vue
+++ b/packages/fes-template/src/pages/onepiece.vue
@@ -3,8 +3,8 @@
 </template>
 <config>
 {
-    "title": "onepiece",
-    "layout": "true"
+    "name": "onepiece",
+    "title": "onepiece"
 }
 </config>
 <script>