vben
2020-10-31 77028321816f00799cc3f70d3f0d6bde27c34522
perf: layout style optimization
27个文件已修改
419 ■■■■ 已修改文件
.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
CHANGELOG.zh_CN.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Breadcrumb/Breadcrumb.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/BasicMenu.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/SearchInput.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/index.less 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/props.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/ant/index.less 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/index.less 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/public.less 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/var/index.less 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/Logo.vue 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutContent.tsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutHeader.tsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutMenu.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutSideBar.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/UserDropdown.tsx 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/actions/LockActionItem.less 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/actions/LockActionItem.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/index.less 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/index.tsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/multitabs/index.less 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/multitabs/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/setting/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/iframe/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/settings/projectSetting.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env
@@ -2,7 +2,7 @@
VITE_PORT = 3100
# spa-title
VITE_GLOB_APP_TITLE = Vben Admin 2.0
VITE_GLOB_APP_TITLE = Vben Admin
# spa shortname
VITE_GLOB_APP_SHORT_NAME = vue_vben_admin_2x
CHANGELOG.zh_CN.md
@@ -1,3 +1,13 @@
## Wip
### ⚡ Performance Improvements
- Layout 界面布局样式调整
### 🐛 Bug Fixes
- 修复表格类型错误
## 2.0.0-rc.7 (2020-10-31)
### ✨ Features
src/assets/images/logo.png

src/components/Breadcrumb/Breadcrumb.vue
@@ -37,7 +37,7 @@
  .breadcrumb {
    height: @header-height;
    padding-right: 20px;
    font-size: 12px;
    font-size: 14px;
    line-height: @header-height;
    // line-height: 1;
src/components/Menu/src/BasicMenu.tsx
@@ -66,7 +66,7 @@
        offset += 54;
      }
      return {
        height: `calc(100% - ${offset - 30}px)`,
        height: `calc(100% - ${offset - 38}px)`,
        position: 'relative',
        overflow: 'auto',
      };
@@ -147,6 +147,7 @@
    }
    const showTitle = computed(() => {
      if (props.isTop) return true;
      if (!props.isAppMenu) return true;
      if (!props.collapsedShowTitle) {
        return !menuStore.getCollapsedState;
@@ -247,7 +248,6 @@
    return () => {
      const { getCollapsedState } = menuStore;
      const { mode } = props;
      return mode === MenuModeEnum.HORIZONTAL ? (
        renderMenu()
      ) : (
src/components/Menu/src/SearchInput.vue
@@ -94,7 +94,7 @@
      .ant-input {
        color: @text-color-base;
        background: #fff;
        border: 0;
        // border: 0;
        outline: none;
        &:hover,
src/components/Menu/src/index.less
@@ -4,7 +4,7 @@
  color: @white;
  background: linear-gradient(
    118deg,
    rgba(@primary-color, 0.7),
    rgba(@primary-color, 0.8),
    rgba(@primary-color, 1)
  ) !important;
  border-radius: 2px;
@@ -32,6 +32,7 @@
      font-size: 12px;
      flex-direction: column;
      line-height: 24px;
      align-items: center;
    }
    & > li[role='menuitem']:not(.ant-menu-submenu) {
@@ -93,6 +94,8 @@
  }
  &-bg__sidebar-hor {
    overflow: hidden;
    &.ant-menu-horizontal {
      display: flex;
      border: 0;
@@ -105,23 +108,16 @@
      &.ant-menu-light {
        .ant-menu-item {
          &.basic-menu-item__level1 {
            height: 38px;
            line-height: 38px;
            height: @header-height;
            line-height: @header-height;
          }
        }
        .ant-menu-item:hover,
        .ant-menu-submenu:hover,
        .ant-menu-item-active,
        .ant-menu-submenu-active,
        .ant-menu-item-open,
        .ant-menu-submenu-open,
        .ant-menu-item-selected,
        .ant-menu-submenu-selected {
          color: @primary-color !important;
          border-bottom: 3px solid @primary-color;
        }
        .ant-menu-submenu-selected,
        .ant-menu-item:hover,
        .ant-menu-item-active,
        .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open,
@@ -261,10 +257,13 @@
  }
  &.ant-menu-light {
    overflow-x: hidden;
    border-right: none;
    .basic-menu-item__level1 {
      &.top-active-menu {
        color: @primary-color;
        border-bottom: 6px solid @primary-color;
        border-bottom: 3px solid @primary-color;
      }
    }
@@ -306,16 +305,9 @@
  }
  &-light {
    border-right: 1px solid rgba(221, 221, 221, 0.6);
    .ant-layout-sider-trigger {
      color: @text-color-base;
      background: @trigger-light-bg-color;
      &:hover {
        color: @text-color-base;
        background: @trigger-light-hover-bg-color;
      }
      border-top: 1px solid @border-color-light;
    }
  }
}
src/components/Menu/src/props.ts
@@ -54,6 +54,10 @@
    type: Boolean as PropType<boolean>,
    default: true,
  },
  isTop: {
    type: Boolean as PropType<boolean>,
    default: false,
  },
  beforeClickFn: {
    type: Function as PropType<Fn>,
    default: null,
src/design/ant/index.less
@@ -2,10 +2,15 @@
@import './input.less';
@import './btn.less';
// TODO beta.11 fix
.ant-col {
  width: 100%;
}
// .ant-form-item-label {
//   text-align: unset;
// }
// =================================
// ==============descriptions=======
// =================================
src/design/index.less
@@ -36,7 +36,8 @@
}
body {
  font-family: 'Microsoft YaHei,微软雅黑,Arial,sans-serif,Helvetica Neue,Helvetica,Pingfang SC,Hiragino Sans GB';
  // font-family: 'Microsoft YaHei,微软雅黑,Arial,sans-serif,Helvetica Neue,Helvetica,Pingfang SC,Hiragino Sans GB';
  font-family: '-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji';
  font-style: normal;
  font-weight: normal;
  line-height: 1.428571429; // 20/14
@@ -149,7 +150,7 @@
}
.ant-layout {
  background: #f1f1f6;
  background: #f0f2f5;
  &-content {
    position: relative;
src/design/public.less
@@ -5,11 +5,11 @@
}
// TODO 滚动条样式-待修改
::-webkit-scrollbar-track {
  // background: rgba(0, 0, 0, 0.06);
  // border-radius: 2px;
  // box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
}
// ::-webkit-scrollbar-track {
//   // background: rgba(0, 0, 0, 0.06);
//   // border-radius: 2px;
//   // box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
// }
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
src/design/var/index.less
@@ -3,13 +3,13 @@
@import 'breakpoint';
// tabs
@multiple-height: 36px;
@multiple-height: 30px;
// headers
@header-height: 36px;
@header-height: 46px;
// logo width
@logo-width: 40px;
@logo-width: 36px;
//
@sider-drag-z-index: 200;
@@ -18,4 +18,4 @@
// app menu
// left-menu
@app-menu-item-height: 44px;
@app-menu-item-height: 42px;
src/layouts/Logo.vue
@@ -1,18 +1,23 @@
<template>
  <div class="app-logo" @click="handleGoHome">
  <div class="app-logo" @click="handleGoHome" :style="wrapStyle">
    <img :src="logo" />
    <div v-if="show" class="logo-title ml-1 mt-1 ellipsis">{{ globSetting.title }}</div>
    <div v-if="show" class="logo-title ml-2 ellipsis">{{ globSetting.title }}</div>
  </div>
</template>
<script lang="ts">
  import { defineComponent, PropType, ref, watch } from 'vue';
  import { computed, defineComponent, PropType, ref, watch } from 'vue';
  // hooks
  import { useSetting } from '/@/hooks/core/useSetting';
  import { PageEnum } from '/@/enums/pageEnum';
  import logo from '/@/assets/images/logo.png';
  import { useTimeout } from '/@/hooks/core/useTimeout';
  import { useGo } from '/@/hooks/web/usePage';
  import { PageEnum } from '/@/enums/pageEnum';
  import { MenuTypeEnum } from '../enums/menuEnum';
  import logo from '/@/assets/images/logo.png';
  import { menuStore } from '../store/modules/menu';
  import { appStore } from '../store/modules/app';
  export default defineComponent({
    name: 'Logo',
@@ -44,11 +49,24 @@
        }
      );
      const wrapStyle = computed(() => {
        const { getCollapsedState } = menuStore;
        const {
          menuSetting: { menuWidth, type },
        } = appStore.getProjectConfig;
        const miniWidth = { minWidth: `${menuWidth}px` };
        if (type !== MenuTypeEnum.SIDEBAR) {
          return miniWidth;
        }
        return getCollapsedState ? {} : miniWidth;
      });
      return {
        handleGoHome,
        globSetting,
        show: showRef,
        logo,
        wrapStyle,
      };
    },
  });
@@ -59,12 +77,13 @@
  .app-logo {
    display: flex;
    align-items: center;
    padding-left: 16px;
    cursor: pointer;
    .logo-title {
      display: none;
      font-family: Georgia, serif;
      font-size: 16px;
      font-weight: 400;
      .respond-to(medium,{
       display: block;
     });
src/layouts/default/LayoutContent.tsx
@@ -4,7 +4,6 @@
import { ContentEnum } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app';
// import { RouterView } from 'vue-router';
import PageLayout from '/@/layouts/page/index';
export default defineComponent({
  name: 'DefaultLayoutContent',
@@ -15,9 +14,7 @@
      const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed';
      return (
        <Layout.Content class={`layout-content ${wrapClass} `}>
          {{
            default: () => <PageLayout />,
          }}
          {() => <PageLayout />}
        </Layout.Content>
      );
    };
src/layouts/default/LayoutHeader.tsx
@@ -1,11 +1,12 @@
import { defineComponent, unref, computed, ref } from 'vue';
import { Layout, Tooltip, Badge } from 'ant-design-vue';
import Logo from '/@/layouts/Logo.vue';
import UserDropdown from './UserDropdown';
import LayoutMenu from './LayoutMenu';
import { appStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import LayoutBreadcrumb from './LayoutBreadcrumb';
import LockAction from './actions/LockActionItem';
import NoticeAction from './actions/notice/NoticeActionItem.vue';
import {
  RedoOutlined,
  FullscreenExitOutlined,
@@ -14,19 +15,24 @@
  LockOutlined,
  BugOutlined,
} from '@ant-design/icons-vue';
import { useFullscreen } from '/@/hooks/web/useFullScreen';
import { useTabs } from '/@/hooks/web/useTabs';
import { GITHUB_URL } from '/@/settings/siteSetting';
import LockAction from './actions/LockActionItem';
import { useModal } from '/@/components/Modal/index';
import { errorStore } from '/@/store/modules/error';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
import NoticeAction from './actions/notice/NoticeActionItem.vue';
import { useRouter } from 'vue-router';
import { useModal } from '/@/components/Modal/index';
import { appStore } from '/@/store/modules/app';
import { errorStore } from '/@/store/modules/error';
import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { GITHUB_URL } from '/@/settings/siteSetting';
export default defineComponent({
  name: 'DefaultLayoutHeader',
  setup() {
    const widthRef = ref(200);
    let logoEl: Element | null;
    const { refreshPage } = useTabs();
    const { push } = useRouter();
    const [register, { openModal }] = useModal();
@@ -35,6 +41,7 @@
    const getProjectConfigRef = computed(() => {
      return appStore.getProjectConfig;
    });
    const showTopMenu = computed(() => {
      const getProjectConfig = unref(getProjectConfigRef);
      const {
@@ -43,7 +50,6 @@
      return mode === MenuModeEnum.HORIZONTAL || splitMenu;
    });
    let logoEl: Element | null;
    useWindowSizeFn(
      () => {
        if (!unref(showTopMenu)) return;
@@ -80,6 +86,7 @@
    function handleLockPage() {
      openModal(true);
    }
    return () => {
      const getProjectConfig = unref(getProjectConfigRef);
      const {
@@ -99,7 +106,9 @@
      } = getProjectConfig;
      const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
      const width = unref(widthRef);
      return (
        <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
          {() => (
@@ -112,10 +121,12 @@
                )}
                {unref(showTopMenu) && (
                  <div
                    class={[`layout-header__menu `, `justify-${topMenuAlign}`]}
                    class={[`layout-header__menu `]}
                    style={{ width: `calc(100% - ${unref(width)}px)` }}
                  >
                    <LayoutMenu
                      isTop={true}
                      class={`justify-${topMenuAlign}`}
                      theme={headerTheme}
                      splitType={splitMenu ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE}
                      menuMode={splitMenu ? MenuModeEnum.HORIZONTAL : null}
src/layouts/default/LayoutMenu.tsx
@@ -45,6 +45,10 @@
      type: Boolean as PropType<boolean>,
      default: true,
    },
    isTop: {
      type: Boolean as PropType<boolean>,
      default: false,
    },
    menuMode: {
      type: [String] as PropType<MenuModeEnum | null>,
      default: '',
@@ -199,6 +203,7 @@
          flatItems={unref(flatMenusRef)}
          onClickSearchInput={handleClickSearchInput}
          appendClass={props.splitType === MenuSplitTyeEnum.TOP}
          isTop={props.isTop}
        >
          {{
            header: () =>
src/layouts/default/LayoutSideBar.tsx
@@ -7,7 +7,7 @@
// import darkMiniIMg from '/@/assets/images/sidebar/dark-mini.png';
// import lightMiniImg from '/@/assets/images/sidebar/light-mini.png';
import darkImg from '/@/assets/images/sidebar/dark.png';
import lightImg from '/@/assets/images/sidebar/light.png';
// import lightImg from '/@/assets/images/sidebar/light.png';
import { appStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuSplitTyeEnum, MenuThemeEnum } from '/@/enums/menuEnum';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
@@ -39,15 +39,16 @@
      // const collapse = unref(collapseRef);
      const theme = unref(getProjectConfigRef).menuSetting.theme;
      if (theme === MenuThemeEnum.LIGHT) {
        // bg = lightImg;
        return {};
      }
      let bg = '';
      if (theme === MenuThemeEnum.DARK) {
        // bg = collapse ? darkMiniIMg : darkImg;
        bg = darkImg;
      }
      if (theme === MenuThemeEnum.LIGHT) {
        bg = lightImg;
        // bg = collapse ? lightMiniImg : lightImg;
      }
      return {
        'background-image': `url(${bg})`,
      };
src/layouts/default/UserDropdown.tsx
@@ -21,6 +21,11 @@
      return appStore.getProjectConfig;
    });
    const getUserInfo = computed(() => {
      const { realName = '', desc } = userStore.getUserInfoState || {};
      return { realName, desc };
    });
    /**
     * @description: 退出登录
     */
@@ -41,10 +46,20 @@
        openDoc();
      }
    }
    const getUserInfo = computed(() => {
      const { realName = '', desc } = userStore.getUserInfoState || {};
      return { realName, desc };
    });
    function renderItem({ icon, text, key }: { icon: string; text: string; key: string }) {
      return (
        <Menu.Item key={key}>
          {() => (
            <span class="flex items-center">
              <Icon icon={icon} class="mr-1" />
              <span>{text}</span>
            </span>
          )}
        </Menu.Item>
      );
    }
    return () => {
      const { realName } = unref(getUserInfo);
      const {
@@ -65,28 +80,13 @@
              <Menu slot="overlay" onClick={handleMenuClick}>
                {() => (
                  <>
                    {showDoc && (
                      <Menu.Item key="doc">
                        {() => (
                          <span class="flex items-center">
                            <Icon icon="gg:loadbar-doc" class="mr-1" />
                            <span>文档</span>
                          </span>
                        )}
                      </Menu.Item>
                    )}
                    {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
                    {showDoc && <Divider />}
                    <Menu.Item key="loginOut">
                      {() => (
                        <>
                          <span class="flex items-center">
                            <Icon icon="ant-design:poweroff-outlined" class="mr-1" />
                            <span>退出系统</span>
                          </span>
                        </>
                      )}
                    </Menu.Item>
                    {renderItem({
                      key: 'loginOut',
                      text: '退出系统',
                      icon: 'ant-design:poweroff-outlined',
                    })}
                  </>
                )}
              </Menu>
src/layouts/default/actions/LockActionItem.less
@@ -1,9 +1,9 @@
.lock-modal {
  &__entry {
    position: relative;
    width: 500px;
    // width: 500px;
    height: 240px;
    padding: 80px 30px 0 30px;
    padding: 130px 30px 60px 30px;
    background: #fff;
    border-radius: 10px;
  }
src/layouts/default/actions/LockActionItem.tsx
@@ -24,7 +24,7 @@
      schemas: [
        {
          field: 'password',
          label: '锁屏密码',
          label: '',
          component: 'InputPassword',
          componentProps: {
            placeholder: '请输入锁屏密码',
src/layouts/default/index.less
@@ -1,12 +1,6 @@
@import (reference) '../../design/index.less';
.default-layout {
  // .ant-menu-submenu .ant-menu-sub {
  //   transition: none !important;
  //   // transition: background 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s,
  //   //   padding 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) 0s !important;
  // }
  &__content {
    position: relative;
@@ -72,6 +66,10 @@
  .layout-sidebar {
    background-size: 100% 100%;
    &:not(.ant-layout-sider-dark) {
      border-right: 1px solid @border-color-light;
    }
    .ant-layout-sider-zero-width-trigger {
      top: 40%;
      z-index: 10;
@@ -99,25 +97,13 @@
    }
  }
  .setting-button {
    top: 45%;
    right: 0;
    padding: 8px;
    border-radius: 6px 0 0 6px;
    svg {
      width: 1em;
      height: 1em;
    }
  }
  &__tabs {
    z-index: 10;
    height: @multiple-height;
    padding: 0;
    line-height: @multiple-height;
    background: @border-color-shallow-light;
    box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.08);
    box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.12);
  }
}
@@ -194,9 +180,15 @@
  }
}
.ant-layout-header {
.ant-layout-header:not(.default-layout__tabs) {
  height: @header-height;
  line-height: @header-height;
}
.ant-layout-header.default-layout__tabs {
  height: @multiple-height + 2;
  line-height: @multiple-height + 2;
  background: @white;
}
.layout-header {
@@ -351,22 +343,20 @@
  }
  &__menu {
    // display: flex;
    margin-left: 20px;
    overflow: hidden;
    align-items: center;
    // flex-grow: 1;
  }
  &__user-dropdown {
    height: 52px;
    height: @header-height;
    padding: 0 0 0 10px;
  }
}
.user-dropdown {
  display: flex;
  height: 100%;
  padding-right: 10px;
  font-size: 12px;
  cursor: pointer;
  align-items: center;
@@ -374,48 +364,11 @@
  img {
    width: 26px;
    height: 26px;
    margin-right: 16px;
    margin-right: 12px;
  }
  &__header {
    border-radius: 50%;
  }
  &__divider {
    width: 1px;
    height: 30px;
    margin-right: 20px;
    background: #c6d9ee;
  }
  &__exit {
    margin-top: -40px;
    font-size: 12px;
    color: #c6d9ee;
    text-align: center;
    > section {
      height: 20px;
    }
  }
  &__info {
    display: flex;
    margin-right: 12px;
    flex-direction: column;
    > section {
      line-height: 1.8;
    }
  }
  &__name {
    font-size: 12px;
  }
  &__desc {
    font-size: 12px;
    .text-truncate();
  }
}
@@ -425,8 +378,8 @@
}
.ant-layout-sider-trigger {
  height: 30px;
  line-height: 30px;
  height: 36px;
  line-height: 36px;
}
.hide-title {
src/layouts/default/index.tsx
@@ -1,4 +1,4 @@
import { defineComponent, unref, onMounted, computed } from 'vue';
import { defineComponent, unref, computed } from 'vue';
import { Layout, BackTop } from 'ant-design-vue';
import LayoutHeader from './LayoutHeader';
@@ -30,6 +30,7 @@
    const getProjectConfigRef = computed(() => {
      return appStore.getProjectConfig;
    });
    const getLockMainScrollStateRef = computed(() => {
      return appStore.getLockMainScrollState;
    });
@@ -40,6 +41,7 @@
      } = unref(getProjectConfigRef);
      return show;
    });
    const isShowMixHeaderRef = computed(() => {
      const {
        menuSetting: { type },
@@ -54,12 +56,6 @@
      return show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent);
    });
    // const { currentRoute } = useRouter();
    onMounted(() => {
      // Each refresh will request the latest user information, if you don’t need it, you can delete it
      // userStore.getUserInfoAction({ userId: userStore.getUserInfoState.userId });
    });
    // Get project configuration
    // const { getFullContent } = useFullContent(currentRoute);
    function getTarget(): any {
@@ -68,6 +64,7 @@
      } = unref(getProjectConfigRef);
      return document.querySelector(`.default-layout__${fixed ? 'main' : 'content'}`);
    }
    return () => {
      const { getPageLoading, getLockInfo } = appStore;
      const {
@@ -77,10 +74,11 @@
        multiTabsSetting: { show: showTabs },
        headerSetting: { fixed },
      } = unref(getProjectConfigRef);
      // const fixedHeaderCls = fixed ? ('fixed' + getLockMainScrollState ? ' lock' : '') : '';
      const fixedHeaderCls = fixed
        ? 'fixed' + (unref(getLockMainScrollStateRef) ? ' lock' : '')
        : '';
      const { isLock } = getLockInfo;
      return (
        <Layout class="default-layout relative">
src/layouts/default/multitabs/index.less
@@ -1,8 +1,6 @@
@import (reference) '../../../design/index.less';
.multiple-tabs {
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  .ant-tabs-small {
    height: @multiple-height;
  }
@@ -13,7 +11,7 @@
      margin: 0;
      background: @white;
      border: 0;
      box-shadow: 0 4px 26px 1px rgba(0, 0, 0, 0.08);
      box-shadow: none;
      .ant-tabs-nav-container {
        height: @multiple-height;
@@ -22,17 +20,26 @@
      .ant-tabs-tab {
        height: calc(@multiple-height - 2px);
        font-size: 14px;
        line-height: calc(@multiple-height - 2px);
        color: @text-color-call-out;
        background: @white;
        border: 1px solid darken(@border-color-light, 6%);
        border-radius: 2px 2px 0 0;
        border: 1px solid darken(@border-color-light, 8%);
        border-radius: none !important;
        transition: none;
        .ant-tabs-close-x {
          // display: none;
          width: 12px;
          height: 12px;
          font-size: 12px;
          color: inherit;
          transition: none;
          &:hover {
            svg {
              width: 0.8em;
              transition: all 0.1s;
            }
          }
        }
        &:hover {
@@ -50,37 +57,12 @@
        svg {
          fill: @text-color-base;
        }
        &::before {
          position: absolute;
          top: -2px;
          right: 0;
          left: 0;
          height: 4px;
          background-color: @primary-color;
          border-radius: 16px 6px 0 0;
          content: '';
          transform: scaleX(0);
          transform-origin: bottom right;
        }
        &:hover::before {
          transform: scaleX(1);
          transition: transform 0.3s ease;
          transform-origin: bottom left;
        }
      }
      .ant-tabs-tab-active {
        height: calc(@multiple-height - 3px);
        color: @white;
        background: linear-gradient(
          118deg,
          rgba(@primary-color, 0.8),
          rgba(@primary-color, 1)
        ) !important;
        background: fade(@primary-color, 100%);
        border: 0;
        box-shadow: 0 0 6px 1px rgba(@primary-color, 0.7);
        &::before {
          display: none;
@@ -88,18 +70,13 @@
        svg {
          fill: @white;
          width: 0.7em;
        }
      }
    }
    .ant-tabs-nav > div:nth-child(1) {
      padding: 0 10px;
    }
    .ant-tabs-tab-prev,
    .ant-tabs-tab-next {
      color: @border-color-dark;
      background: @white;
    }
  }
@@ -108,20 +85,19 @@
      font-size: 12px;
      svg {
        width: 0.8em;
      }
    }
    &:hover {
      .anticon-close {
        color: @white;
        width: 0.6em;
      }
    }
  }
}
.ant-tabs-extra-content {
  line-height: @multiple-height;
  margin-top: 2px;
  line-height: @multiple-height !important;
}
.ant-dropdown-trigger {
  display: inline-flex;
}
.multiple-tabs-content {
@@ -133,7 +109,7 @@
    color: @primary-color;
    text-align: center;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
    // box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
    span[role='img'] {
      transform: rotate(90deg);
@@ -143,8 +119,10 @@
  &__content {
    display: inline-block;
    width: 100%;
    padding-left: 10px;
    height: @multiple-height - 2;
    padding-left: 0;
    margin-left: -10px;
    font-size: 12px;
    cursor: pointer;
    user-select: none;
  }
src/layouts/default/multitabs/index.tsx
@@ -159,7 +159,7 @@
            size="small"
            animated={false}
            hideAdd={true}
            tabBarGutter={2}
            tabBarGutter={4}
            activeKey={unref(activeKeyRef)}
            onChange={handleChange}
            onEdit={handleEdit}
src/layouts/default/setting/index.vue
@@ -28,13 +28,21 @@
  .setting-button {
    position: absolute;
    top: 45%;
    right: 0;
    z-index: 10;
    display: flex;
    // padding: 10px;
    padding: 10px;
    color: @white;
    cursor: pointer;
    background: @primary-color;
    border-radius: 6px 0 0 6px;
    justify-content: center;
    align-items: center;
    svg {
      width: 1em;
      height: 1em;
    }
  }
</style>
src/layouts/iframe/index.vue
@@ -9,14 +9,15 @@
</template>
<script lang="ts">
  import { defineComponent } from 'vue';
  import { useFrameKeepAlive } from './useFrameKeepAlive';
  import FramePage from '/@/views/sys/iframe/index.vue';
  import { useFrameKeepAlive } from './useFrameKeepAlive';
  export default defineComponent({
    name: 'FrameLayout',
    components: { FramePage },
    setup() {
      const { hasRenderFrame, showIframe, getFramePages } = useFrameKeepAlive();
      return { hasRenderFrame, showIframe, getFramePages };
      return { ...useFrameKeepAlive() };
    },
  });
</script>
src/settings/projectSetting.ts
@@ -55,7 +55,7 @@
    // 是否显示搜索框
    showSearch: true,
    // 菜单宽度
    menuWidth: 180,
    menuWidth: 200,
    // 菜单模式
    mode: MenuModeEnum.INLINE,
    // 菜单类型
@@ -65,7 +65,7 @@
    // 分割菜单
    split: false,
    // 顶部菜单布局
    topMenuAlign: 'start',
    topMenuAlign: 'center',
  },
  // 消息配置
  messageSetting: {
@@ -83,7 +83,7 @@
    // 开启快速操作
    showQuick: true,
    // 显示icon
    showIcon: true,
    showIcon: false,
    // 标签页缓存最大数量
    max: 12,
  },