perf: optimize settingDrawer code
| | |
| | | |
| | | - 表单项的`componentsProps`支持函数类型 |
| | | |
| | | ### ⚡ Performance Improvements |
| | | |
| | | - 优化 settingDrawer 代码 |
| | | |
| | | ### 🐛 Bug Fixes |
| | | |
| | | - 修复多个富文本编辑器只显示一个 |
| | |
| | | .app-loading { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | /* background: #f0f2f5; */ |
| | | background: #f0f2f5; |
| | | } |
| | | |
| | | .app-loading .app-loading-wrap { |
| | |
| | | address: '@city()', |
| | | name: '@cname()', |
| | | 'no|100000-10000000': 100000, |
| | | 'status|1': ['正常', '启用', '停用'], |
| | | 'status|1': ['normal', 'enable', 'disable'], |
| | | }); |
| | | } |
| | | return result; |
| | |
| | | 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({ |
| | |
| | | import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel'; |
| | | /** |
| | | * @description: 请求列表接口参数 |
| | | * @description: Request list interface parameters |
| | | */ |
| | | export type DemoParams = BasicPageParams; |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * @description: 请求列表返回值 |
| | | * @description: Request list return value |
| | | */ |
| | | export type DemoListGetResultModel = BasicFetchResult<DemoListItem>; |
| | |
| | | } |
| | | |
| | | /** |
| | | * @description: 获取示例列表值 |
| | | * @description: Get sample list value |
| | | */ |
| | | export function demoListApi(params: DemoParams) { |
| | | return defHttp.request<DemoListGetResultModel>({ |
| | |
| | | | 'InputNumber' |
| | | | 'InputCountDown' |
| | | | 'Select' |
| | | | 'DictSelect' |
| | | | 'SelectOptGroup' |
| | | | 'SelectOption' |
| | | | 'TreeSelect' |
| | |
| | | toRef(props, 'items'), |
| | | toRef(props, 'flatItems'), |
| | | toRef(props, 'isAppMenu'), |
| | | toRef(props, 'mode') |
| | | toRef(props, 'mode'), |
| | | toRef(props, 'accordion') |
| | | ); |
| | | |
| | | const getOpenKeys = computed(() => { |
| | |
| | | type: Boolean as PropType<boolean>, |
| | | default: false, |
| | | }, |
| | | accordion: { |
| | | type: Boolean as PropType<boolean>, |
| | | default: true, |
| | | }, |
| | | beforeClickFn: { |
| | | type: Function as PropType<Fn>, |
| | | default: null, |
| | |
| | | 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: 重置值 |
| | | */ |
| | |
| | | } |
| | | |
| | | function handleOpenChange(openKeys: string[]) { |
| | | if (unref(mode) === MenuModeEnum.HORIZONTAL) { |
| | | if (unref(mode) === MenuModeEnum.HORIZONTAL || !unref(accordion)) { |
| | | menuState.openKeys = openKeys; |
| | | } else { |
| | | const rootSubMenuKeys: string[] = []; |
| | |
| | | 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() { |
| | |
| | | return ( |
| | | <Layout.Content class={`layout-content ${wrapClass} `}> |
| | | {() => <RouterView />} |
| | | {/* <PageLayout class={`layout-content ${wrapClass} `} /> */} |
| | | </Layout.Content> |
| | | ); |
| | | }; |
| | |
| | | }, |
| | | }, |
| | | 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]) => { |
| | |
| | | } |
| | | ); |
| | | |
| | | // 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) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 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(); |
| | |
| | | return; |
| | | } |
| | | |
| | | // 菜单分割模式-top |
| | | // split-top |
| | | if (splitType === MenuSplitTyeEnum.TOP) { |
| | | const parentPath = await getCurrentParentPath(unref(currentRoute).path); |
| | | menuStore.commitCurrentTopSplitMenuPathState(parentPath); |
| | |
| | | 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); |
| | | } |
| | | } |
| | | |
| | |
| | | collapsed, |
| | | collapsedShowTitle, |
| | | collapsedShowSearch, |
| | | accordion, |
| | | }, |
| | | } = unref(getProjectConfigRef); |
| | | |
| | |
| | | onClickSearchInput={handleClickSearchInput} |
| | | appendClass={props.splitType === MenuSplitTyeEnum.TOP} |
| | | isTop={props.isTop} |
| | | accordion={accordion} |
| | | > |
| | | {{ |
| | | header: () => |
| | |
| | | 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'; |
| | |
| | | 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); |
| | |
| | | const side = unref(sideRef); |
| | | |
| | | const wrap = (side || {}).$el; |
| | | // const eleWidth = 6; |
| | | ele && |
| | | (ele.onmousedown = (e: any) => { |
| | | menuStore.commitDragStartState(true); |
| | |
| | | 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(() => { |
| | |
| | | return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent)); |
| | | }); |
| | | |
| | | // Get project configuration |
| | | // const { getFullContent } = useFullContent(currentRoute); |
| | | function getTarget(): any { |
| | | const { |
| | | headerSetting: { fixed }, |
| | |
| | | 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'; |
| | |
| | | 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>; |
| | |
| | | }); |
| | | } |
| | | |
| | | 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 }, |
| | |
| | | {{ |
| | | default: () => ( |
| | | <div |
| | | onClick={baseHandler.bind(null, 'layout', { |
| | | onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, { |
| | | mode: mode, |
| | | type: ItemType, |
| | | split: unref(getIsHorizontalRef) ? false : undefined, |
| | |
| | | </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, |
| | |
| | | }), |
| | | renderSelectItem('菜单主题', { |
| | | handler: (e) => { |
| | | baseHandler('menuTheme', e); |
| | | baseHandler(HandlerEnum.MENU_THEME, e); |
| | | }, |
| | | def: menuTheme, |
| | | options: themeOptions, |
| | |
| | | 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, |
| | |
| | | }), |
| | | 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, |
| | |
| | | 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) => { |
| | |
| | | defaultValue={menuWidth} |
| | | formatter={(value: string) => `${parseInt(value)}px`} |
| | | onChange={(e: any) => { |
| | | baseHandler('menuWidth', e); |
| | | baseHandler(HandlerEnum.MENU_WIDTH, e); |
| | | }} |
| | | /> |
| | | </div>, |
| | |
| | | <> |
| | | {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, |
| | |
| | | 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) { |
| | |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | return () => ( |
| | | <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer"> |
| | | {{ |
New file |
| | |
| | | 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, |
| | | }; |
| | | }); |
New file |
| | |
| | | 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 {}; |
| | | } |
| | | } |
| | |
| | | collapsedShowSearch: false, |
| | | // 折叠触发器的位置 |
| | | trigger: TriggerEnum.HEADER, |
| | | // 开启手风琴模式,只显示一个菜单 |
| | | accordion: true, |
| | | }, |
| | | // 消息配置 |
| | | messageSetting: { |
| | |
| | | topMenuAlign: 'start' | 'center' | 'end'; |
| | | collapsedShowSearch: boolean; |
| | | trigger: TriggerEnum; |
| | | accordion: boolean; |
| | | } |
| | | |
| | | export interface MultiTabsSetting { |
| | |
| | | |
| | | 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 = { |
| | |
| | | }; |
| | | } |
| | | |
| | | // 初始化项目配置 |
| | | // Initial project configuration |
| | | export function useInitAppConfigStore() { |
| | | let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig; |
| | | if (!projCfg) { |
| | |
| | | }; |
| | | } |
| | | |
| | | // 初始化网络监听 |
| | | // 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); |
| | |
| | | <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"> |
| | |
| | | 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, |
| | |
| | | <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'; |
| | |
| | | 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 {}; |
| | | }, |
| | |
| | | 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;}); |
| | |
| | | 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 { |
| | |
| | | * @default 'index.html' |
| | | */ |
| | | // TODO build error |
| | | // entry: './public/index.html', |
| | | // entry: 'public/index.html', |
| | | /** |
| | | * port |
| | | * @default '3000' |