vben
2020-11-10 4ff6b73c2bb57764db2bcd8212d82f028e25e36d
perf: optimize settingDrawer code
2个文件已添加
23个文件已修改
853 ■■■■ 已修改文件
CHANGELOG.zh_CN.md 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mock/demo/table-demo.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/demo/error.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/demo/model/tableModel.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/demo/table.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Form/src/types/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/BasicMenu.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/props.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Menu/src/useOpenKeys.ts 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutContent.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutMenu.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/LayoutSideBar.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/setting/SettingDrawer.tsx 392 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/setting/const.ts 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/setting/handler.ts 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/settings/projectSetting.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/types/config.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/types/global.d.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/useApp.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/analysis/index.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/workbench/index.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sys/login/Login.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
CHANGELOG.zh_CN.md
@@ -4,6 +4,10 @@
- 表单项的`componentsProps`支持函数类型
### ⚡ Performance Improvements
- 优化 settingDrawer 代码
### 🐛 Bug Fixes
- 修复多个富文本编辑器只显示一个
index.html
@@ -30,8 +30,7 @@
      .app-loading {
        width: 100%;
        height: 100%;
        /* background: #f0f2f5; */
        background: #f0f2f5;
      }
      .app-loading .app-loading-wrap {
mock/demo/table-demo.ts
@@ -11,7 +11,7 @@
      address: '@city()',
      name: '@cname()',
      'no|100000-10000000': 100000,
      'status|1': ['正常', '启用', '停用'],
      'status|1': ['normal', 'enable', 'disable'],
    });
  }
  return result;
src/api/demo/error.ts
@@ -1,12 +1,12 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
  // 该地址不存在
  // The address does not exist
  Error = '/error',
}
/**
 * @description: 触发ajax错误
 * @description: Trigger ajax error
 */
export function fireErrorApi() {
  return defHttp.request({
src/api/demo/model/tableModel.ts
@@ -1,6 +1,6 @@
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
/**
 * @description: 请求列表接口参数
 * @description: Request list interface parameters
 */
export type DemoParams = BasicPageParams;
@@ -15,6 +15,6 @@
}
/**
 * @description: 请求列表返回值
 * @description: Request list return value
 */
export type DemoListGetResultModel = BasicFetchResult<DemoListItem>;
src/api/demo/table.ts
@@ -6,7 +6,7 @@
}
/**
 * @description: 获取示例列表值
 * @description: Get sample list value
 */
export function demoListApi(params: DemoParams) {
  return defHttp.request<DemoListGetResultModel>({
src/components/Form/src/types/index.ts
@@ -89,7 +89,6 @@
  | 'InputNumber'
  | 'InputCountDown'
  | 'Select'
  | 'DictSelect'
  | 'SelectOptGroup'
  | 'SelectOption'
  | 'TreeSelect'
src/components/Menu/src/BasicMenu.tsx
@@ -52,7 +52,8 @@
      toRef(props, 'items'),
      toRef(props, 'flatItems'),
      toRef(props, 'isAppMenu'),
      toRef(props, 'mode')
      toRef(props, 'mode'),
      toRef(props, 'accordion')
    );
    const getOpenKeys = computed(() => {
src/components/Menu/src/props.ts
@@ -58,6 +58,10 @@
    type: Boolean as PropType<boolean>,
    default: false,
  },
  accordion: {
    type: Boolean as PropType<boolean>,
    default: true,
  },
  beforeClickFn: {
    type: Function as PropType<Fn>,
    default: null,
src/components/Menu/src/useOpenKeys.ts
@@ -6,21 +6,31 @@
import { unref } from 'vue';
import { menuStore } from '/@/store/modules/menu';
import { getAllParentPath } from '/@/utils/helper/menuHelper';
import { es6Unique } from '/@/utils';
export function useOpenKeys(
  menuState: MenuState,
  menus: Ref<MenuType[]>,
  flatMenusRef: Ref<MenuType[]>,
  isAppMenu: Ref<boolean>,
  mode: Ref<MenuModeEnum>
  mode: Ref<MenuModeEnum>,
  accordion: Ref<boolean>
) {
  /**
   * @description:设置展开
   */
  function setOpenKeys(menu: MenuType) {
    const flatMenus = unref(flatMenusRef);
    if (!unref(accordion)) {
      menuState.openKeys = es6Unique([
        ...menuState.openKeys,
        ...getAllParentPath(flatMenus, menu.path),
      ]);
    } else {
    menuState.openKeys = getAllParentPath(flatMenus, menu.path);
  }
  }
  /**
   * @description:  重置值
   */
@@ -30,7 +40,7 @@
  }
  function handleOpenChange(openKeys: string[]) {
    if (unref(mode) === MenuModeEnum.HORIZONTAL) {
    if (unref(mode) === MenuModeEnum.HORIZONTAL || !unref(accordion)) {
      menuState.openKeys = openKeys;
    } else {
      const rootSubMenuKeys: string[] = [];
src/layouts/default/LayoutContent.tsx
@@ -2,11 +2,8 @@
import { Layout } from 'ant-design-vue';
import { RouterView } from 'vue-router';
// hooks
import { ContentEnum } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app';
// import PageLayout from '/@/layouts/page/index';
export default defineComponent({
  name: 'DefaultLayoutContent',
  setup() {
@@ -17,7 +14,6 @@
      return (
        <Layout.Content class={`layout-content ${wrapClass} `}>
          {() => <RouterView />}
          {/* <PageLayout class={`layout-content ${wrapClass} `} /> */}
        </Layout.Content>
      );
    };
src/layouts/default/LayoutMenu.tsx
@@ -55,27 +55,25 @@
    },
  },
  setup(props) {
    // Menu array
    const menusRef = ref<Menu[]>([]);
    // flat menu array
    const flatMenusRef = ref<Menu[]>([]);
    const { currentRoute, push } = useRouter();
    // const { addTab } = useTabs();
    // get app config
    const getProjectConfigRef = computed(() => {
      return appStore.getProjectConfig;
    });
    // get is Horizontal
    const getIsHorizontalRef = computed(() => {
      return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
    });
    const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
    // watch(
    //   () => menuStore.getCurrentTopSplitMenuPathState,
    //   async (parentPath: string) => {
    //     throttleHandleSplitLeftMenu(parentPath);
    //   }
    // );
    // Route change split menu
    watch(
      [() => unref(currentRoute).path, () => props.splitType],
      async ([path, splitType]: [string, MenuSplitTyeEnum]) => {
@@ -88,23 +86,26 @@
      }
    );
    // Menu changes
    watch(
      [() => permissionStore.getLastBuildMenuTimeState, permissionStore.getBackMenuListState],
      [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
      () => {
        genMenus();
      }
    );
    // split Menu changes
    watch([() => appStore.getProjectConfig.menuSetting.split], () => {
      if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
      genMenus();
    });
    // Handle left menu split
    async function handleSplitLeftMenu(parentPath: string) {
      const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
      if (!isSplitMenu) return;
      const { splitType } = props;
      // 菜单分割模式-left
      // spilt mode left
      if (splitType === MenuSplitTyeEnum.LEFT) {
        const children = await getChildrenMenus(parentPath);
        if (!children) {
@@ -128,11 +129,11 @@
      }
    }
    // get menus
    async function genMenus() {
      const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
      // 普通模式
      // normal mode
      const { splitType } = props;
      if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) {
        flatMenusRef.value = await getFlatMenus();
@@ -140,7 +141,7 @@
        return;
      }
      // 菜单分割模式-top
      // split-top
      if (splitType === MenuSplitTyeEnum.TOP) {
        const parentPath = await getCurrentParentPath(unref(currentRoute).path);
        menuStore.commitCurrentTopSplitMenuPathState(parentPath);
@@ -156,12 +157,11 @@
      const { path } = menu;
      if (path) {
        const { splitType } = props;
        // 菜单分割模式-top
        // split mode top
        if (splitType === MenuSplitTyeEnum.TOP) {
          menuStore.commitCurrentTopSplitMenuPathState(path);
        }
        push(path);
        // addTab(path as PageEnum, true);
      }
    }
@@ -205,6 +205,7 @@
          collapsed,
          collapsedShowTitle,
          collapsedShowSearch,
          accordion,
        },
      } = unref(getProjectConfigRef);
@@ -227,6 +228,7 @@
          onClickSearchInput={handleClickSearchInput}
          appendClass={props.splitType === MenuSplitTyeEnum.TOP}
          isTop={props.isTop}
          accordion={accordion}
        >
          {{
            header: () =>
src/layouts/default/LayoutSideBar.tsx
@@ -4,9 +4,6 @@
import LayoutTrigger from './LayoutTrigger';
import { menuStore } from '/@/store/modules/menu';
// import darkMiniIMg from '/@/assets/images/sidebar/dark-mini.png';
// import lightMiniImg from '/@/assets/images/sidebar/light-mini.png';
// import lightImg from '/@/assets/images/sidebar/light.png';
import { appStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
@@ -44,7 +41,7 @@
      initRef.value = true;
    }
    // 菜单区域拖拽 - 鼠标移动
    // Menu area drag and drop-mouse movement
    function handleMouseMove(ele: any, wrap: any, clientX: number) {
      document.onmousemove = function (innerE) {
        let iT = ele.left + ((innerE || event).clientX - clientX);
@@ -98,7 +95,6 @@
      const side = unref(sideRef);
      const wrap = (side || {}).$el;
      // const eleWidth = 6;
      ele &&
        (ele.onmousedown = (e: any) => {
          menuStore.commitDragStartState(true);
src/layouts/default/index.tsx
@@ -19,12 +19,11 @@
export default defineComponent({
  name: 'DefaultLayout',
  setup() {
    // ! 在这里才注册全局组件
    // ! 可以减少首屏代码体积
    // default layout是在登录后才加载的。所以不会打包到首屏去
    // ! Only register global components here
    // ! Can reduce the size of the first screen code
    // default layout It is loaded after login. So it won’t be packaged to the first screen
    registerGlobComp();
    // 获取项目配置
    const { getFullContent } = useFullContent();
    const getProjectConfigRef = computed(() => {
@@ -56,8 +55,6 @@
      return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent));
    });
    // Get project configuration
    // const { getFullContent } = useFullContent(currentRoute);
    function getTarget(): any {
      const {
        headerSetting: { fixed },
src/layouts/default/setting/SettingDrawer.tsx
@@ -2,14 +2,7 @@
import { BasicDrawer } from '/@/components/Drawer/index';
import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
import Button from '/@/components/Button/index.vue';
import {
  MenuModeEnum,
  MenuTypeEnum,
  MenuThemeEnum,
  TopMenuAlignEnum,
  TriggerEnum,
} from '/@/enums/menuEnum';
import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { appStore } from '/@/store/modules/app';
import { userStore } from '/@/store/modules/user';
@@ -24,70 +17,15 @@
import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
import menuTopImg from '/@/assets/images/layout/menu-top.svg';
import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
const themeOptions = [
  {
    value: MenuThemeEnum.LIGHT,
    label: '亮色',
  },
  {
    value: MenuThemeEnum.DARK,
    label: '暗色',
  },
];
const contentModeOptions = [
  {
    value: ContentEnum.FULL,
    label: '流式',
  },
  {
    value: ContentEnum.FIXED,
    label: '定宽',
  },
];
const topMenuAlignOptions = [
  {
    value: TopMenuAlignEnum.CENTER,
    label: '居中',
  },
  {
    value: TopMenuAlignEnum.START,
    label: '居左',
  },
  {
    value: TopMenuAlignEnum.END,
    label: '居右',
  },
];
const menuTriggerOptions = [
  {
    value: TriggerEnum.NONE,
    label: '不显示',
  },
  {
    value: TriggerEnum.FOOTER,
    label: '底部',
  },
  {
    value: TriggerEnum.HEADER,
    label: '顶部',
  },
];
const routerTransitionOptions = [
  RouterTransitionEnum.ZOOM_FADE,
  RouterTransitionEnum.FADE,
  RouterTransitionEnum.ZOOM_OUT,
  RouterTransitionEnum.FADE_SIDE,
  RouterTransitionEnum.FADE_BOTTOM,
].map((item) => {
  return {
    label: item,
    value: item,
    key: item,
  };
});
import { baseHandler } from './handler';
import {
  HandlerEnum,
  themeOptions,
  contentModeOptions,
  topMenuAlignOptions,
  menuTriggerOptions,
  routerTransitionOptions,
} from './const';
interface SwitchOptions {
  config?: DeepPartial<ProjectConfig>;
@@ -139,6 +77,25 @@
        });
    }
    function handleResetSetting() {
      try {
        appStore.commitProjectConfigState(defaultSetting);
        const { colorWeak, grayMode } = defaultSetting;
        // updateTheme(themeColor);
        updateColorWeak(colorWeak);
        updateGrayMode(grayMode);
        createMessage.success('重置成功!');
      } catch (error) {
        createMessage.error(error);
      }
    }
    function handleClearAndRedo() {
      localStorage.clear();
      userStore.resumeAllState();
      location.reload();
    }
    function renderSidebar() {
      const {
        headerSetting: { theme: headerTheme },
@@ -175,7 +132,7 @@
                {{
                  default: () => (
                    <div
                      onClick={baseHandler.bind(null, 'layout', {
                      onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
                        mode: mode,
                        type: ItemType,
                        split: unref(getIsHorizontalRef) ? false : undefined,
@@ -192,14 +149,14 @@
        </div>,
        renderSwitchItem('分割菜单', {
          handler: (e) => {
            baseHandler('splitMenu', e);
            baseHandler(HandlerEnum.MENU_SPLIT, e);
          },
          def: split,
          disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
        }),
        renderSelectItem('顶栏主题', {
          handler: (e) => {
            baseHandler('headerMenu', e);
            baseHandler(HandlerEnum.HEADER_THEME, e);
          },
          def: headerTheme,
          options: themeOptions,
@@ -207,7 +164,7 @@
        }),
        renderSelectItem('菜单主题', {
          handler: (e) => {
            baseHandler('menuTheme', e);
            baseHandler(HandlerEnum.MENU_THEME, e);
          },
          def: menuTheme,
          options: themeOptions,
@@ -230,48 +187,49 @@
          topMenuAlign,
          collapsedShowTitle,
          trigger,
          accordion,
        } = {},
      } = appStore.getProjectConfig;
      return [
        renderSwitchItem('侧边菜单拖拽', {
          handler: (e) => {
            baseHandler('hasDrag', e);
            baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
          },
          def: hasDrag,
          disabled: !unref(getShowMenuRef),
        }),
        renderSwitchItem('侧边菜单搜索', {
          handler: (e) => {
            baseHandler('showSearch', e);
            baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
          },
          def: showSearch,
          disabled: !unref(getShowMenuRef),
        }),
        renderSwitchItem('侧边菜单手风琴模式', {
          handler: (e) => {
            baseHandler(HandlerEnum.MENU_ACCORDION, e);
          },
          def: accordion,
          disabled: !unref(getShowMenuRef),
        }),
        renderSwitchItem('折叠菜单', {
          handler: (e) => {
            baseHandler('collapsed', e);
            baseHandler(HandlerEnum.MENU_COLLAPSED, e);
          },
          def: collapsed,
          disabled: !unref(getShowMenuRef),
        }),
        renderSwitchItem('折叠菜单显示名称', {
          handler: (e) => {
            baseHandler('collapsedShowTitle', e);
            baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
          },
          def: collapsedShowTitle,
          disabled: !unref(getShowMenuRef) || !collapsed,
        }),
        renderSwitchItem('固定header', {
          handler: (e) => {
            baseHandler('headerFixed', e);
          },
          def: fixed,
          disabled: !unref(getShowHeaderRef),
        }),
        renderSelectItem('顶部菜单布局', {
          handler: (e) => {
            baseHandler('topMenuAlign', e);
            baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
          },
          def: topMenuAlign,
          options: topMenuAlignOptions,
@@ -279,14 +237,21 @@
        }),
        renderSelectItem('菜单折叠按钮', {
          handler: (e) => {
            baseHandler('menuTrigger', e);
            baseHandler(HandlerEnum.MENU_TRIGGER, e);
          },
          def: trigger,
          options: menuTriggerOptions,
        }),
        renderSwitchItem('固定header', {
          handler: (e) => {
            baseHandler(HandlerEnum.HEADER_FIXED, e);
          },
          def: fixed,
          disabled: !unref(getShowHeaderRef),
        }),
        renderSelectItem('内容区域宽度', {
          handler: (e) => {
            baseHandler('contentMode', e);
            baseHandler(HandlerEnum.CONTENT_MODE, e);
          },
          def: contentMode,
          options: contentModeOptions,
@@ -297,8 +262,8 @@
            style="width:120px"
            size="small"
            min={0}
            onChange={(e) => {
              baseHandler('lockTime', e);
            onChange={(e: any) => {
              baseHandler(HandlerEnum.LOCK_TIME, e);
            }}
            defaultValue={appStore.getProjectConfig.lockTime}
            formatter={(value: string) => {
@@ -321,7 +286,7 @@
            defaultValue={menuWidth}
            formatter={(value: string) => `${parseInt(value)}px`}
            onChange={(e: any) => {
              baseHandler('menuWidth', e);
              baseHandler(HandlerEnum.MENU_WIDTH, e);
            }}
          />
        </div>,
@@ -334,19 +299,19 @@
        <>
          {renderSwitchItem('页面切换loading', {
            handler: (e) => {
              baseHandler('openPageLoading', e);
              baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
            },
            def: openPageLoading,
          })}
          {renderSwitchItem('切换动画', {
            handler: (e) => {
              baseHandler('openRouterTransition', e);
              baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
            },
            def: openRouterTransition,
          })}
          {renderSelectItem('路由动画', {
            handler: (e) => {
              baseHandler('routerTransition', e);
              baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
            },
            def: routerTransition,
            options: routerTransitionOptions,
@@ -370,288 +335,76 @@
      return [
        renderSwitchItem('面包屑', {
          handler: (e) => {
            baseHandler('showBreadCrumb', e);
            baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
          },
          def: showBreadCrumb,
          disabled: !unref(getShowHeaderRef),
        }),
        renderSwitchItem('面包屑图标', {
          handler: (e) => {
            baseHandler('showBreadCrumbIcon', e);
            baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
          },
          def: showBreadCrumbIcon,
          disabled: !unref(getShowHeaderRef),
        }),
        renderSwitchItem('标签页', {
          handler: (e) => {
            baseHandler('showMultiple', e);
            baseHandler(HandlerEnum.TABS_SHOW, e);
          },
          def: showMultiple,
        }),
        renderSwitchItem('标签页快捷按钮', {
          handler: (e) => {
            baseHandler('showQuick', e);
            baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
          },
          def: showQuick,
          disabled: !unref(getShowTabsRef),
        }),
        renderSwitchItem('标签页图标', {
          handler: (e) => {
            baseHandler('showTabIcon', e);
            baseHandler(HandlerEnum.TABS_SHOW_ICON, e);
          },
          def: showTabIcon,
          disabled: !unref(getShowTabsRef),
        }),
        renderSwitchItem('左侧菜单', {
          handler: (e) => {
            baseHandler('showSidebar', e);
            baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
          },
          def: showMenu,
          disabled: unref(getIsHorizontalRef),
        }),
        renderSwitchItem('顶栏', {
          handler: (e) => {
            baseHandler('showHeader', e);
            baseHandler(HandlerEnum.HEADER_SHOW, e);
          },
          def: showHeader,
        }),
        renderSwitchItem('Logo', {
          handler: (e) => {
            baseHandler('showLogo', e);
            baseHandler(HandlerEnum.SHOW_LOGO, e);
          },
          def: showLogo,
        }),
        renderSwitchItem('全屏内容', {
          handler: (e) => {
            baseHandler('fullContent', e);
            baseHandler(HandlerEnum.FULL_CONTENT, e);
          },
          def: fullContent,
        }),
        renderSwitchItem('灰色模式', {
          handler: (e) => {
            baseHandler('grayMode', e);
            baseHandler(HandlerEnum.GRAY_MODE, e);
          },
          def: grayMode,
        }),
        renderSwitchItem('色弱模式', {
          handler: (e) => {
            baseHandler('colorWeak', e);
            baseHandler(HandlerEnum.COLOR_WEAK, e);
          },
          def: colorWeak,
        }),
      ];
    }
    function baseHandler(event: string, value: any) {
      let config: DeepPartial<ProjectConfig> = {};
      if (event === 'layout') {
        const { mode, type, split } = value;
        const splitOpt = split === undefined ? { split } : {};
        let headerSetting = {};
        if (type === MenuTypeEnum.TOP_MENU) {
          headerSetting = {
            theme: MenuThemeEnum.DARK,
          };
        }
        config = {
          menuSetting: {
            mode,
            type,
            collapsed: false,
            show: true,
            ...splitOpt,
          },
          headerSetting,
        };
      }
      if (event === 'hasDrag') {
        config = {
          menuSetting: {
            hasDrag: value,
          },
        };
      }
      if (event === 'menuTrigger') {
        config = {
          menuSetting: {
            trigger: value,
          },
        };
      }
      if (event === 'openPageLoading') {
        config = {
          openPageLoading: value,
        };
      }
      if (event === 'topMenuAlign') {
        config = {
          menuSetting: {
            topMenuAlign: value,
          },
        };
      }
      if (event === 'showBreadCrumb') {
        config = {
          showBreadCrumb: value,
        };
      }
      if (event === 'showBreadCrumbIcon') {
        config = {
          showBreadCrumbIcon: value,
        };
      }
      if (event === 'collapsed') {
        config = {
          menuSetting: {
            collapsed: value,
          },
        };
      }
      if (event === 'menuWidth') {
        config = {
          menuSetting: {
            menuWidth: value,
          },
        };
      }
      if (event === 'collapsedShowTitle') {
        config = {
          menuSetting: {
            collapsedShowTitle: value,
          },
        };
      }
      if (event === 'lockTime') {
        config = {
          lockTime: value,
        };
      }
      if (event === 'showQuick') {
        config = {
          multiTabsSetting: {
            showQuick: value,
          },
        };
      }
      if (event === 'showTabIcon') {
        config = {
          multiTabsSetting: {
            showIcon: value,
          },
        };
      }
      if (event === 'contentMode') {
        config = {
          contentMode: value,
        };
      }
      if (event === 'menuTheme') {
        config = {
          menuSetting: {
            theme: value,
          },
        };
      }
      if (event === 'splitMenu') {
        config = {
          menuSetting: {
            split: value,
          },
        };
      }
      if (event === 'showMultiple') {
        config = {
          multiTabsSetting: {
            show: value,
          },
        };
      }
      if (event === 'headerMenu') {
        config = {
          headerSetting: {
            theme: value,
          },
        };
      }
      if (event === 'grayMode') {
        config = {
          grayMode: value,
        };
        updateGrayMode(value);
      }
      if (event === 'colorWeak') {
        config = {
          colorWeak: value,
        };
        updateColorWeak(value);
      }
      if (event === 'showLogo') {
        config = {
          showLogo: value,
        };
      }
      if (event === 'showSearch') {
        config = {
          menuSetting: {
            showSearch: value,
          },
        };
      }
      if (event === 'showSidebar') {
        config = {
          menuSetting: {
            show: value,
          },
        };
      }
      if (event === 'openRouterTransition') {
        config = {
          openRouterTransition: value,
        };
      }
      if (event === 'routerTransition') {
        config = {
          routerTransition: value,
        };
      }
      if (event === 'headerFixed') {
        config = {
          headerSetting: {
            fixed: value,
          },
        };
      }
      if (event === 'fullContent') {
        config = {
          fullContent: value,
        };
      }
      if (event === 'showHeader') {
        config = {
          headerSetting: {
            show: value,
          },
        };
      }
      appStore.commitProjectConfigState(config);
    }
    function handleResetSetting() {
      try {
        appStore.commitProjectConfigState(defaultSetting);
        const { colorWeak, grayMode } = defaultSetting;
        // updateTheme(themeColor);
        updateColorWeak(colorWeak);
        updateGrayMode(grayMode);
        createMessage.success('重置成功!');
      } catch (error) {
        createMessage.error(error);
      }
    }
    function handleClearAndRedo() {
      localStorage.clear();
      userStore.resumeAllState();
      location.reload();
    }
    function renderSelectItem(text: string, config?: SelectConfig) {
@@ -693,6 +446,7 @@
        </div>
      );
    }
    return () => (
      <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
        {{
src/layouts/default/setting/const.ts
New file
@@ -0,0 +1,104 @@
import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuThemeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
export enum HandlerEnum {
  CHANGE_LAYOUT,
  // menu
  MENU_HAS_DRAG,
  MENU_ACCORDION,
  MENU_TRIGGER,
  MENU_TOP_ALIGN,
  MENU_COLLAPSED,
  MENU_COLLAPSED_SHOW_TITLE,
  MENU_WIDTH,
  MENU_SHOW_SIDEBAR,
  MENU_THEME,
  MENU_SPLIT,
  MENU_SHOW_SEARCH,
  // header
  HEADER_SHOW,
  HEADER_THEME,
  HEADER_FIXED,
  TABS_SHOW_QUICK,
  TABS_SHOW,
  TABS_SHOW_ICON,
  OPEN_PAGE_LOADING,
  OPEN_ROUTE_TRANSITION,
  ROUTER_TRANSITION,
  LOCK_TIME,
  FULL_CONTENT,
  CONTENT_MODE,
  SHOW_BREADCRUMB,
  SHOW_BREADCRUMB_ICON,
  GRAY_MODE,
  COLOR_WEAK,
  SHOW_LOGO,
}
export const themeOptions = [
  {
    value: MenuThemeEnum.LIGHT,
    label: '亮色',
  },
  {
    value: MenuThemeEnum.DARK,
    label: '暗色',
  },
];
export const contentModeOptions = [
  {
    value: ContentEnum.FULL,
    label: '流式',
  },
  {
    value: ContentEnum.FIXED,
    label: '定宽',
  },
];
export const topMenuAlignOptions = [
  {
    value: TopMenuAlignEnum.CENTER,
    label: '居中',
  },
  {
    value: TopMenuAlignEnum.START,
    label: '居左',
  },
  {
    value: TopMenuAlignEnum.END,
    label: '居右',
  },
];
export const menuTriggerOptions = [
  {
    value: TriggerEnum.NONE,
    label: '不显示',
  },
  {
    value: TriggerEnum.FOOTER,
    label: '底部',
  },
  {
    value: TriggerEnum.HEADER,
    label: '顶部',
  },
];
export const routerTransitionOptions = [
  RouterTransitionEnum.ZOOM_FADE,
  RouterTransitionEnum.FADE,
  RouterTransitionEnum.ZOOM_OUT,
  RouterTransitionEnum.FADE_SIDE,
  RouterTransitionEnum.FADE_BOTTOM,
].map((item) => {
  return {
    label: item,
    value: item,
  };
});
src/layouts/default/setting/handler.ts
New file
@@ -0,0 +1,186 @@
import { HandlerEnum } from './const';
import { MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
import { appStore } from '/@/store/modules/app';
import { ProjectConfig } from '/@/types/config';
export function baseHandler(event: HandlerEnum, value: any) {
  const config = handler(event, value);
  appStore.commitProjectConfigState(config);
}
export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConfig> {
  switch (event) {
    case HandlerEnum.CHANGE_LAYOUT:
      const { mode, type, split } = value;
      const splitOpt = split === undefined ? { split } : {};
      let headerSetting = {};
      if (type === MenuTypeEnum.TOP_MENU) {
        headerSetting = {
          theme: MenuThemeEnum.DARK,
        };
      }
      return {
        menuSetting: {
          mode,
          type,
          collapsed: false,
          show: true,
          ...splitOpt,
        },
        headerSetting,
      };
    case HandlerEnum.MENU_HAS_DRAG:
      return {
        menuSetting: {
          hasDrag: value,
        },
      };
    case HandlerEnum.MENU_ACCORDION:
      return {
        menuSetting: {
          accordion: value,
        },
      };
    case HandlerEnum.MENU_TRIGGER:
      return {
        menuSetting: {
          trigger: value,
        },
      };
    case HandlerEnum.MENU_TOP_ALIGN:
      return {
        menuSetting: {
          topMenuAlign: value,
        },
      };
    case HandlerEnum.MENU_COLLAPSED:
      return {
        menuSetting: {
          collapsed: value,
        },
      };
    case HandlerEnum.MENU_WIDTH:
      return {
        menuSetting: {
          menuWidth: value,
        },
      };
    case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE:
      return {
        menuSetting: {
          collapsedShowTitle: value,
        },
      };
    case HandlerEnum.MENU_SHOW_SIDEBAR:
      return {
        menuSetting: {
          show: value,
        },
      };
    case HandlerEnum.MENU_THEME:
      return {
        menuSetting: {
          theme: value,
        },
      };
    case HandlerEnum.MENU_SPLIT:
      return {
        menuSetting: {
          split: value,
        },
      };
    case HandlerEnum.MENU_SHOW_SEARCH:
      return {
        menuSetting: {
          showSearch: value,
        },
      };
    case HandlerEnum.OPEN_PAGE_LOADING:
      return {
        openPageLoading: value,
      };
    case HandlerEnum.OPEN_ROUTE_TRANSITION:
      return {
        openRouterTransition: value,
      };
    case HandlerEnum.ROUTER_TRANSITION:
      return {
        routerTransition: value,
      };
    case HandlerEnum.LOCK_TIME:
      return {
        lockTime: value,
      };
    case HandlerEnum.FULL_CONTENT:
      return {
        fullContent: value,
      };
    case HandlerEnum.CONTENT_MODE:
      return {
        contentMode: value,
      };
    case HandlerEnum.SHOW_BREADCRUMB:
      return {
        showBreadCrumb: value,
      };
    case HandlerEnum.SHOW_BREADCRUMB_ICON:
      return {
        showBreadCrumbIcon: value,
      };
    case HandlerEnum.GRAY_MODE:
      updateGrayMode(value);
      return {
        grayMode: value,
      };
    case HandlerEnum.COLOR_WEAK:
      updateColorWeak(value);
      return {
        colorWeak: value,
      };
    case HandlerEnum.SHOW_LOGO:
      return {
        showLogo: value,
      };
    case HandlerEnum.TABS_SHOW_QUICK:
      return {
        multiTabsSetting: {
          showQuick: value,
        },
      };
    case HandlerEnum.TABS_SHOW_QUICK:
      return {
        multiTabsSetting: {
          showIcon: value,
        },
      };
    case HandlerEnum.TABS_SHOW:
      return {
        multiTabsSetting: {
          show: value,
        },
      };
    case HandlerEnum.HEADER_THEME:
      return {
        headerSetting: {
          theme: value,
        },
      };
    case HandlerEnum.HEADER_FIXED:
      return {
        headerSetting: {
          fixed: value,
        },
      };
    case HandlerEnum.HEADER_SHOW:
      return {
        headerSetting: {
          show: value,
        },
      };
    default:
      return {};
  }
}
src/settings/projectSetting.ts
@@ -74,6 +74,8 @@
    collapsedShowSearch: false,
    // 折叠触发器的位置
    trigger: TriggerEnum.HEADER,
    // 开启手风琴模式,只显示一个菜单
    accordion: true,
  },
  // 消息配置
  messageSetting: {
src/types/config.d.ts
@@ -24,6 +24,7 @@
  topMenuAlign: 'start' | 'center' | 'end';
  collapsedShowSearch: boolean;
  trigger: TriggerEnum;
  accordion: boolean;
}
export interface MultiTabsSetting {
src/types/global.d.ts
@@ -30,12 +30,16 @@
declare type Hash<T> = Indexable<T>;
// declare type DeepPartial<T> = {
//   [P in keyof T]?: T[P] extends (infer U)[]
//     ? RecursivePartial<U>[]
//     : T[P] extends object
//     ? RecursivePartial<T[P]>
//     : T[P];
// };
declare type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends (infer U)[]
    ? RecursivePartial<U>[]
    : T[P] extends object
    ? RecursivePartial<T[P]>
    : T[P];
  [P in keyof T]?: DeepPartial<T[P]>;
};
declare type SelectOptions = {
src/useApp.ts
@@ -42,7 +42,7 @@
  };
}
// 初始化项目配置
// Initial project configuration
export function useInitAppConfigStore() {
  let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig;
  if (!projCfg) {
@@ -78,12 +78,12 @@
  };
}
// 初始化网络监听
// Initialize network monitoring
export function useListenerNetWork() {
  const { listenNetWork } = appStore.getProjectConfig;
  if (!listenNetWork) return;
  const { replace } = useRouter();
  // 检测网络状态
  // Check network status
  useNetWork({
    onLineFn: () => {
      replace(PageEnum.BASE_HOME);
src/views/dashboard/analysis/index.vue
@@ -1,42 +1,42 @@
<template>
  <div class="analysis p-4">
    <Row class="pl-2">
    <a-row class="pl-2">
      <template v-for="item in growCardList" :key="item.title">
        <ACol :sm="24" :md="12" :lg="6">
          <GrowCard :info="item" />
        </ACol>
      </template>
    </Row>
    </a-row>
    <Row>
      <ACol :md="24" :lg="17" class="my-3">
    <a-row>
      <a-col :md="24" :lg="17" class="my-3">
        <CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
          <AnalysisLine />
        </CollapseContainer>
        <Row class="mt-3">
          <ACol :md="24" :lg="12" class="product-total">
        <a-row class="mt-3">
          <a-col :md="24" :lg="12" class="product-total">
            <CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
              <AnalysisPie />
            </CollapseContainer>
          </ACol>
          <ACol :md="24" :lg="12">
          </a-col>
          <a-col :md="24" :lg="12">
            <CollapseContainer class="mr-3" title="用户来源" :canExpan="false">
              <AnalysisBar />
            </CollapseContainer>
          </ACol>
        </Row>
      </ACol>
      <ACol :md="24" :lg="7">
          </a-col>
        </a-row>
      </a-col>
      <a-col :md="24" :lg="7">
        <CollapseContainer class="mt-3" title="项目进度" :canExpan="false">
          <template v-for="item in taskList" :key="item.title">
            <TaskCard :info="item" />
          </template>
        </CollapseContainer>
      </ACol>
    </Row>
    <Row>
      </a-col>
    </a-row>
    <a-row>
      <FlowAnalysis />
    </Row>
    </a-row>
  </div>
</template>
<script lang="ts">
@@ -48,14 +48,11 @@
  import AnalysisBar from './components/AnalysisBar.vue';
  import TaskCard from './components/TaskCard.vue';
  import FlowAnalysis from './components/FlowAnalysis';
  import { Row, Col } from 'ant-design-vue';
  import { CollapseContainer } from '/@/components/Container/index';
  import { growCardList, taskList } from './data';
  export default defineComponent({
    components: {
      Row,
      ACol: Col,
      GrowCard,
      CollapseContainer,
      TrendLine,
src/views/dashboard/workbench/index.vue
@@ -1,20 +1,19 @@
<template>
  <Row class="workbench p-4" :gutter="12">
    <Col :md="24" :lg="17">
  <a-row class="workbench p-4" :gutter="12">
    <a-col :md="24" :lg="17">
      <ProdTotal class="mb-3" />
      <TodoList class="mb-3" />
      <NewsList class="mb-3" />
    </Col>
    <Col :md="24" :lg="7">
    </a-col>
    <a-col :md="24" :lg="7">
      <img src="/@/assets/images/dashboard/wokb/wokb.png" class="workbench__wokb-img mb-3" />
      <ShortCuts class="mb-3" />
      <Week class="mb-3" />
    </Col>
  </Row>
    </a-col>
  </a-row>
</template>
<script lang="ts">
  import { defineComponent } from 'vue';
  import { Row, Col } from 'ant-design-vue';
  import ProdTotal from './components/ProdTotal.vue';
  import TodoList from './components/TodoList.vue';
  import Week from './components/Week.vue';
@@ -22,7 +21,7 @@
  import ShortCuts from './components/ShortCuts.vue';
  export default defineComponent({
    components: { Row, Col, ProdTotal, TodoList, Week, ShortCuts, NewsList },
    components: { ProdTotal, TodoList, Week, ShortCuts, NewsList },
    setup() {
      return {};
    },
src/views/sys/login/Login.vue
@@ -168,7 +168,7 @@
      display: none;
      height: 100%;
      background: url(../../../assets/images/login/login-in.png) no-repeat;
      background-position: 50% 30%;
      background-position: 30% 30%;
      background-size: 80% 80%;
      .respond-to(xlarge, { display: block;});
@@ -194,9 +194,9 @@
        align-items: center;
        .respond-to(large, {
          width: 600px;
          right: calc(50% - 300px);
          right: calc(50% - 270px);
          });
        .respond-to(xlarge, { width: 600px; right:0});
        .respond-to(xlarge, { width: 540px; right:0});
      }
      &__content {
vite.config.ts
@@ -34,7 +34,7 @@
   * @default 'index.html'
   */
  // TODO build error
  // entry: './public/index.html',
  // entry: 'public/index.html',
  /**
   * port
   * @default '3000'