perf: optimize settingDrawer code

This commit is contained in:
vben 2020-11-10 22:45:39 +08:00
parent 5832ee6697
commit 4ff6b73c2b
25 changed files with 455 additions and 400 deletions

View File

@ -4,6 +4,10 @@
- 表单项的`componentsProps`支持函数类型 - 表单项的`componentsProps`支持函数类型
### ⚡ Performance Improvements
- 优化 settingDrawer 代码
### 🐛 Bug Fixes ### 🐛 Bug Fixes
- 修复多个富文本编辑器只显示一个 - 修复多个富文本编辑器只显示一个

View File

@ -30,8 +30,7 @@
.app-loading { .app-loading {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #f0f2f5;
/* background: #f0f2f5; */
} }
.app-loading .app-loading-wrap { .app-loading .app-loading-wrap {

View File

@ -11,7 +11,7 @@ const demoList = (() => {
address: '@city()', address: '@city()',
name: '@cname()', name: '@cname()',
'no|100000-10000000': 100000, 'no|100000-10000000': 100000,
'status|1': ['正常', '启用', '停用'], 'status|1': ['normal', 'enable', 'disable'],
}); });
} }
return result; return result;

View File

@ -1,12 +1,12 @@
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
enum Api { enum Api {
// 该地址不存在 // The address does not exist
Error = '/error', Error = '/error',
} }
/** /**
* @description: ajax错误 * @description: Trigger ajax error
*/ */
export function fireErrorApi() { export function fireErrorApi() {
return defHttp.request({ return defHttp.request({

View File

@ -1,6 +1,6 @@
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel'; import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
/** /**
* @description: * @description: Request list interface parameters
*/ */
export type DemoParams = BasicPageParams; export type DemoParams = BasicPageParams;
@ -15,6 +15,6 @@ export interface DemoListItem {
} }
/** /**
* @description: * @description: Request list return value
*/ */
export type DemoListGetResultModel = BasicFetchResult<DemoListItem>; export type DemoListGetResultModel = BasicFetchResult<DemoListItem>;

View File

@ -6,7 +6,7 @@ enum Api {
} }
/** /**
* @description: * @description: Get sample list value
*/ */
export function demoListApi(params: DemoParams) { export function demoListApi(params: DemoParams) {
return defHttp.request<DemoListGetResultModel>({ return defHttp.request<DemoListGetResultModel>({

View File

@ -89,7 +89,6 @@ export type ComponentType =
| 'InputNumber' | 'InputNumber'
| 'InputCountDown' | 'InputCountDown'
| 'Select' | 'Select'
| 'DictSelect'
| 'SelectOptGroup' | 'SelectOptGroup'
| 'SelectOption' | 'SelectOption'
| 'TreeSelect' | 'TreeSelect'

View File

@ -52,7 +52,8 @@ export default defineComponent({
toRef(props, 'items'), toRef(props, 'items'),
toRef(props, 'flatItems'), toRef(props, 'flatItems'),
toRef(props, 'isAppMenu'), toRef(props, 'isAppMenu'),
toRef(props, 'mode') toRef(props, 'mode'),
toRef(props, 'accordion')
); );
const getOpenKeys = computed(() => { const getOpenKeys = computed(() => {

View File

@ -58,6 +58,10 @@ export const basicProps = {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
}, },
accordion: {
type: Boolean as PropType<boolean>,
default: true,
},
beforeClickFn: { beforeClickFn: {
type: Function as PropType<Fn>, type: Function as PropType<Fn>,
default: null, default: null,

View File

@ -6,21 +6,31 @@ import type { Ref } from 'vue';
import { unref } from 'vue'; import { unref } from 'vue';
import { menuStore } from '/@/store/modules/menu'; import { menuStore } from '/@/store/modules/menu';
import { getAllParentPath } from '/@/utils/helper/menuHelper'; import { getAllParentPath } from '/@/utils/helper/menuHelper';
import { es6Unique } from '/@/utils';
export function useOpenKeys( export function useOpenKeys(
menuState: MenuState, menuState: MenuState,
menus: Ref<MenuType[]>, menus: Ref<MenuType[]>,
flatMenusRef: Ref<MenuType[]>, flatMenusRef: Ref<MenuType[]>,
isAppMenu: Ref<boolean>, isAppMenu: Ref<boolean>,
mode: Ref<MenuModeEnum> mode: Ref<MenuModeEnum>,
accordion: Ref<boolean>
) { ) {
/** /**
* @description: * @description:
*/ */
function setOpenKeys(menu: MenuType) { function setOpenKeys(menu: MenuType) {
const flatMenus = unref(flatMenusRef); const flatMenus = unref(flatMenusRef);
menuState.openKeys = getAllParentPath(flatMenus, menu.path); if (!unref(accordion)) {
menuState.openKeys = es6Unique([
...menuState.openKeys,
...getAllParentPath(flatMenus, menu.path),
]);
} else {
menuState.openKeys = getAllParentPath(flatMenus, menu.path);
}
} }
/** /**
* @description: * @description:
*/ */
@ -30,7 +40,7 @@ export function useOpenKeys(
} }
function handleOpenChange(openKeys: string[]) { function handleOpenChange(openKeys: string[]) {
if (unref(mode) === MenuModeEnum.HORIZONTAL) { if (unref(mode) === MenuModeEnum.HORIZONTAL || !unref(accordion)) {
menuState.openKeys = openKeys; menuState.openKeys = openKeys;
} else { } else {
const rootSubMenuKeys: string[] = []; const rootSubMenuKeys: string[] = [];

View File

@ -2,11 +2,8 @@ import { defineComponent } from 'vue';
import { Layout } from 'ant-design-vue'; import { Layout } from 'ant-design-vue';
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
// hooks
import { ContentEnum } from '/@/enums/appEnum'; import { ContentEnum } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
// import PageLayout from '/@/layouts/page/index';
export default defineComponent({ export default defineComponent({
name: 'DefaultLayoutContent', name: 'DefaultLayoutContent',
setup() { setup() {
@ -17,7 +14,6 @@ export default defineComponent({
return ( return (
<Layout.Content class={`layout-content ${wrapClass} `}> <Layout.Content class={`layout-content ${wrapClass} `}>
{() => <RouterView />} {() => <RouterView />}
{/* <PageLayout class={`layout-content ${wrapClass} `} /> */}
</Layout.Content> </Layout.Content>
); );
}; };

View File

@ -55,27 +55,25 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
// Menu array
const menusRef = ref<Menu[]>([]); const menusRef = ref<Menu[]>([]);
// flat menu array
const flatMenusRef = ref<Menu[]>([]); const flatMenusRef = ref<Menu[]>([]);
const { currentRoute, push } = useRouter(); const { currentRoute, push } = useRouter();
// const { addTab } = useTabs();
// get app config
const getProjectConfigRef = computed(() => { const getProjectConfigRef = computed(() => {
return appStore.getProjectConfig; return appStore.getProjectConfig;
}); });
// get is Horizontal
const getIsHorizontalRef = computed(() => { const getIsHorizontalRef = computed(() => {
return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL; return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
}); });
const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50); const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
// watch( // Route change split menu
// () => menuStore.getCurrentTopSplitMenuPathState,
// async (parentPath: string) => {
// throttleHandleSplitLeftMenu(parentPath);
// }
// );
watch( watch(
[() => unref(currentRoute).path, () => props.splitType], [() => unref(currentRoute).path, () => props.splitType],
async ([path, splitType]: [string, MenuSplitTyeEnum]) => { async ([path, splitType]: [string, MenuSplitTyeEnum]) => {
@ -88,23 +86,26 @@ export default defineComponent({
} }
); );
// Menu changes
watch( watch(
[() => permissionStore.getLastBuildMenuTimeState, permissionStore.getBackMenuListState], [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
() => { () => {
genMenus(); genMenus();
} }
); );
// split Menu changes
watch([() => appStore.getProjectConfig.menuSetting.split], () => { watch([() => appStore.getProjectConfig.menuSetting.split], () => {
if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return; if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
genMenus(); genMenus();
}); });
// Handle left menu split
async function handleSplitLeftMenu(parentPath: string) { async function handleSplitLeftMenu(parentPath: string) {
const isSplitMenu = unref(getProjectConfigRef).menuSetting.split; const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
if (!isSplitMenu) return; if (!isSplitMenu) return;
const { splitType } = props; const { splitType } = props;
// 菜单分割模式-left // spilt mode left
if (splitType === MenuSplitTyeEnum.LEFT) { if (splitType === MenuSplitTyeEnum.LEFT) {
const children = await getChildrenMenus(parentPath); const children = await getChildrenMenus(parentPath);
if (!children) { if (!children) {
@ -128,11 +129,11 @@ export default defineComponent({
} }
} }
// get menus
async function genMenus() { async function genMenus() {
const isSplitMenu = unref(getProjectConfigRef).menuSetting.split; const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
// 普通模式 // normal mode
const { splitType } = props; const { splitType } = props;
if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) { if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) {
flatMenusRef.value = await getFlatMenus(); flatMenusRef.value = await getFlatMenus();
@ -140,7 +141,7 @@ export default defineComponent({
return; return;
} }
// 菜单分割模式-top // split-top
if (splitType === MenuSplitTyeEnum.TOP) { if (splitType === MenuSplitTyeEnum.TOP) {
const parentPath = await getCurrentParentPath(unref(currentRoute).path); const parentPath = await getCurrentParentPath(unref(currentRoute).path);
menuStore.commitCurrentTopSplitMenuPathState(parentPath); menuStore.commitCurrentTopSplitMenuPathState(parentPath);
@ -156,12 +157,11 @@ export default defineComponent({
const { path } = menu; const { path } = menu;
if (path) { if (path) {
const { splitType } = props; const { splitType } = props;
// 菜单分割模式-top // split mode top
if (splitType === MenuSplitTyeEnum.TOP) { if (splitType === MenuSplitTyeEnum.TOP) {
menuStore.commitCurrentTopSplitMenuPathState(path); menuStore.commitCurrentTopSplitMenuPathState(path);
} }
push(path); push(path);
// addTab(path as PageEnum, true);
} }
} }
@ -205,6 +205,7 @@ export default defineComponent({
collapsed, collapsed,
collapsedShowTitle, collapsedShowTitle,
collapsedShowSearch, collapsedShowSearch,
accordion,
}, },
} = unref(getProjectConfigRef); } = unref(getProjectConfigRef);
@ -227,6 +228,7 @@ export default defineComponent({
onClickSearchInput={handleClickSearchInput} onClickSearchInput={handleClickSearchInput}
appendClass={props.splitType === MenuSplitTyeEnum.TOP} appendClass={props.splitType === MenuSplitTyeEnum.TOP}
isTop={props.isTop} isTop={props.isTop}
accordion={accordion}
> >
{{ {{
header: () => header: () =>

View File

@ -4,9 +4,6 @@ import { Layout } from 'ant-design-vue';
import LayoutTrigger from './LayoutTrigger'; import LayoutTrigger from './LayoutTrigger';
import { menuStore } from '/@/store/modules/menu'; 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 { appStore } from '/@/store/modules/app';
import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
@ -44,7 +41,7 @@ export default defineComponent({
initRef.value = true; initRef.value = true;
} }
// 菜单区域拖拽 - 鼠标移动 // Menu area drag and drop-mouse movement
function handleMouseMove(ele: any, wrap: any, clientX: number) { function handleMouseMove(ele: any, wrap: any, clientX: number) {
document.onmousemove = function (innerE) { document.onmousemove = function (innerE) {
let iT = ele.left + ((innerE || event).clientX - clientX); let iT = ele.left + ((innerE || event).clientX - clientX);
@ -98,7 +95,6 @@ export default defineComponent({
const side = unref(sideRef); const side = unref(sideRef);
const wrap = (side || {}).$el; const wrap = (side || {}).$el;
// const eleWidth = 6;
ele && ele &&
(ele.onmousedown = (e: any) => { (ele.onmousedown = (e: any) => {
menuStore.commitDragStartState(true); menuStore.commitDragStartState(true);

View File

@ -19,12 +19,11 @@ import './index.less';
export default defineComponent({ export default defineComponent({
name: 'DefaultLayout', name: 'DefaultLayout',
setup() { setup() {
// ! 在这里才注册全局组件 // ! Only register global components here
// ! 可以减少首屏代码体积 // ! Can reduce the size of the first screen code
// default layout是在登录后才加载的。所以不会打包到首屏去 // default layout It is loaded after login. So it wont be packaged to the first screen
registerGlobComp(); registerGlobComp();
// 获取项目配置
const { getFullContent } = useFullContent(); const { getFullContent } = useFullContent();
const getProjectConfigRef = computed(() => { const getProjectConfigRef = computed(() => {
@ -56,8 +55,6 @@ export default defineComponent({
return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent)); return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent));
}); });
// Get project configuration
// const { getFullContent } = useFullContent(currentRoute);
function getTarget(): any { function getTarget(): any {
const { const {
headerSetting: { fixed }, headerSetting: { fixed },

View File

@ -2,14 +2,7 @@ import { defineComponent, computed, unref, ref } from 'vue';
import { BasicDrawer } from '/@/components/Drawer/index'; import { BasicDrawer } from '/@/components/Drawer/index';
import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue'; import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
import Button from '/@/components/Button/index.vue'; import Button from '/@/components/Button/index.vue';
import { import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
MenuModeEnum,
MenuTypeEnum,
MenuThemeEnum,
TopMenuAlignEnum,
TriggerEnum,
} from '/@/enums/menuEnum';
import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { userStore } from '/@/store/modules/user'; import { userStore } from '/@/store/modules/user';
@ -24,70 +17,15 @@ import mixImg from '/@/assets/images/layout/menu-mix.svg';
import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg'; import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
import menuTopImg from '/@/assets/images/layout/menu-top.svg'; import menuTopImg from '/@/assets/images/layout/menu-top.svg';
import { updateColorWeak, updateGrayMode } from '/@/setup/theme'; import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
import { baseHandler } from './handler';
const themeOptions = [ import {
{ HandlerEnum,
value: MenuThemeEnum.LIGHT, themeOptions,
label: '亮色', contentModeOptions,
}, topMenuAlignOptions,
{ menuTriggerOptions,
value: MenuThemeEnum.DARK, routerTransitionOptions,
label: '暗色', } from './const';
},
];
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,
};
});
interface SwitchOptions { interface SwitchOptions {
config?: DeepPartial<ProjectConfig>; config?: DeepPartial<ProjectConfig>;
@ -139,6 +77,25 @@ export default defineComponent({
}); });
} }
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() { function renderSidebar() {
const { const {
headerSetting: { theme: headerTheme }, headerSetting: { theme: headerTheme },
@ -175,7 +132,7 @@ export default defineComponent({
{{ {{
default: () => ( default: () => (
<div <div
onClick={baseHandler.bind(null, 'layout', { onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
mode: mode, mode: mode,
type: ItemType, type: ItemType,
split: unref(getIsHorizontalRef) ? false : undefined, split: unref(getIsHorizontalRef) ? false : undefined,
@ -192,14 +149,14 @@ export default defineComponent({
</div>, </div>,
renderSwitchItem('分割菜单', { renderSwitchItem('分割菜单', {
handler: (e) => { handler: (e) => {
baseHandler('splitMenu', e); baseHandler(HandlerEnum.MENU_SPLIT, e);
}, },
def: split, def: split,
disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX, disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
}), }),
renderSelectItem('顶栏主题', { renderSelectItem('顶栏主题', {
handler: (e) => { handler: (e) => {
baseHandler('headerMenu', e); baseHandler(HandlerEnum.HEADER_THEME, e);
}, },
def: headerTheme, def: headerTheme,
options: themeOptions, options: themeOptions,
@ -207,7 +164,7 @@ export default defineComponent({
}), }),
renderSelectItem('菜单主题', { renderSelectItem('菜单主题', {
handler: (e) => { handler: (e) => {
baseHandler('menuTheme', e); baseHandler(HandlerEnum.MENU_THEME, e);
}, },
def: menuTheme, def: menuTheme,
options: themeOptions, options: themeOptions,
@ -230,48 +187,49 @@ export default defineComponent({
topMenuAlign, topMenuAlign,
collapsedShowTitle, collapsedShowTitle,
trigger, trigger,
accordion,
} = {}, } = {},
} = appStore.getProjectConfig; } = appStore.getProjectConfig;
return [ return [
renderSwitchItem('侧边菜单拖拽', { renderSwitchItem('侧边菜单拖拽', {
handler: (e) => { handler: (e) => {
baseHandler('hasDrag', e); baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
}, },
def: hasDrag, def: hasDrag,
disabled: !unref(getShowMenuRef), disabled: !unref(getShowMenuRef),
}), }),
renderSwitchItem('侧边菜单搜索', { renderSwitchItem('侧边菜单搜索', {
handler: (e) => { handler: (e) => {
baseHandler('showSearch', e); baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
}, },
def: showSearch, def: showSearch,
disabled: !unref(getShowMenuRef), disabled: !unref(getShowMenuRef),
}), }),
renderSwitchItem('侧边菜单手风琴模式', {
handler: (e) => {
baseHandler(HandlerEnum.MENU_ACCORDION, e);
},
def: accordion,
disabled: !unref(getShowMenuRef),
}),
renderSwitchItem('折叠菜单', { renderSwitchItem('折叠菜单', {
handler: (e) => { handler: (e) => {
baseHandler('collapsed', e); baseHandler(HandlerEnum.MENU_COLLAPSED, e);
}, },
def: collapsed, def: collapsed,
disabled: !unref(getShowMenuRef), disabled: !unref(getShowMenuRef),
}), }),
renderSwitchItem('折叠菜单显示名称', { renderSwitchItem('折叠菜单显示名称', {
handler: (e) => { handler: (e) => {
baseHandler('collapsedShowTitle', e); baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
}, },
def: collapsedShowTitle, def: collapsedShowTitle,
disabled: !unref(getShowMenuRef) || !collapsed, disabled: !unref(getShowMenuRef) || !collapsed,
}), }),
renderSwitchItem('固定header', {
handler: (e) => {
baseHandler('headerFixed', e);
},
def: fixed,
disabled: !unref(getShowHeaderRef),
}),
renderSelectItem('顶部菜单布局', { renderSelectItem('顶部菜单布局', {
handler: (e) => { handler: (e) => {
baseHandler('topMenuAlign', e); baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
}, },
def: topMenuAlign, def: topMenuAlign,
options: topMenuAlignOptions, options: topMenuAlignOptions,
@ -279,14 +237,21 @@ export default defineComponent({
}), }),
renderSelectItem('菜单折叠按钮', { renderSelectItem('菜单折叠按钮', {
handler: (e) => { handler: (e) => {
baseHandler('menuTrigger', e); baseHandler(HandlerEnum.MENU_TRIGGER, e);
}, },
def: trigger, def: trigger,
options: menuTriggerOptions, options: menuTriggerOptions,
}), }),
renderSwitchItem('固定header', {
handler: (e) => {
baseHandler(HandlerEnum.HEADER_FIXED, e);
},
def: fixed,
disabled: !unref(getShowHeaderRef),
}),
renderSelectItem('内容区域宽度', { renderSelectItem('内容区域宽度', {
handler: (e) => { handler: (e) => {
baseHandler('contentMode', e); baseHandler(HandlerEnum.CONTENT_MODE, e);
}, },
def: contentMode, def: contentMode,
options: contentModeOptions, options: contentModeOptions,
@ -297,8 +262,8 @@ export default defineComponent({
style="width:120px" style="width:120px"
size="small" size="small"
min={0} min={0}
onChange={(e) => { onChange={(e: any) => {
baseHandler('lockTime', e); baseHandler(HandlerEnum.LOCK_TIME, e);
}} }}
defaultValue={appStore.getProjectConfig.lockTime} defaultValue={appStore.getProjectConfig.lockTime}
formatter={(value: string) => { formatter={(value: string) => {
@ -321,7 +286,7 @@ export default defineComponent({
defaultValue={menuWidth} defaultValue={menuWidth}
formatter={(value: string) => `${parseInt(value)}px`} formatter={(value: string) => `${parseInt(value)}px`}
onChange={(e: any) => { onChange={(e: any) => {
baseHandler('menuWidth', e); baseHandler(HandlerEnum.MENU_WIDTH, e);
}} }}
/> />
</div>, </div>,
@ -334,19 +299,19 @@ export default defineComponent({
<> <>
{renderSwitchItem('页面切换loading', { {renderSwitchItem('页面切换loading', {
handler: (e) => { handler: (e) => {
baseHandler('openPageLoading', e); baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
}, },
def: openPageLoading, def: openPageLoading,
})} })}
{renderSwitchItem('切换动画', { {renderSwitchItem('切换动画', {
handler: (e) => { handler: (e) => {
baseHandler('openRouterTransition', e); baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
}, },
def: openRouterTransition, def: openRouterTransition,
})} })}
{renderSelectItem('路由动画', { {renderSelectItem('路由动画', {
handler: (e) => { handler: (e) => {
baseHandler('routerTransition', e); baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
}, },
def: routerTransition, def: routerTransition,
options: routerTransitionOptions, options: routerTransitionOptions,
@ -370,289 +335,77 @@ export default defineComponent({
return [ return [
renderSwitchItem('面包屑', { renderSwitchItem('面包屑', {
handler: (e) => { handler: (e) => {
baseHandler('showBreadCrumb', e); baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
}, },
def: showBreadCrumb, def: showBreadCrumb,
disabled: !unref(getShowHeaderRef), disabled: !unref(getShowHeaderRef),
}), }),
renderSwitchItem('面包屑图标', { renderSwitchItem('面包屑图标', {
handler: (e) => { handler: (e) => {
baseHandler('showBreadCrumbIcon', e); baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
}, },
def: showBreadCrumbIcon, def: showBreadCrumbIcon,
disabled: !unref(getShowHeaderRef), disabled: !unref(getShowHeaderRef),
}), }),
renderSwitchItem('标签页', { renderSwitchItem('标签页', {
handler: (e) => { handler: (e) => {
baseHandler('showMultiple', e); baseHandler(HandlerEnum.TABS_SHOW, e);
}, },
def: showMultiple, def: showMultiple,
}), }),
renderSwitchItem('标签页快捷按钮', { renderSwitchItem('标签页快捷按钮', {
handler: (e) => { handler: (e) => {
baseHandler('showQuick', e); baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
}, },
def: showQuick, def: showQuick,
disabled: !unref(getShowTabsRef), disabled: !unref(getShowTabsRef),
}), }),
renderSwitchItem('标签页图标', { renderSwitchItem('标签页图标', {
handler: (e) => { handler: (e) => {
baseHandler('showTabIcon', e); baseHandler(HandlerEnum.TABS_SHOW_ICON, e);
}, },
def: showTabIcon, def: showTabIcon,
disabled: !unref(getShowTabsRef), disabled: !unref(getShowTabsRef),
}), }),
renderSwitchItem('左侧菜单', { renderSwitchItem('左侧菜单', {
handler: (e) => { handler: (e) => {
baseHandler('showSidebar', e); baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
}, },
def: showMenu, def: showMenu,
disabled: unref(getIsHorizontalRef), disabled: unref(getIsHorizontalRef),
}), }),
renderSwitchItem('顶栏', { renderSwitchItem('顶栏', {
handler: (e) => { handler: (e) => {
baseHandler('showHeader', e); baseHandler(HandlerEnum.HEADER_SHOW, e);
}, },
def: showHeader, def: showHeader,
}), }),
renderSwitchItem('Logo', { renderSwitchItem('Logo', {
handler: (e) => { handler: (e) => {
baseHandler('showLogo', e); baseHandler(HandlerEnum.SHOW_LOGO, e);
}, },
def: showLogo, def: showLogo,
}), }),
renderSwitchItem('全屏内容', { renderSwitchItem('全屏内容', {
handler: (e) => { handler: (e) => {
baseHandler('fullContent', e); baseHandler(HandlerEnum.FULL_CONTENT, e);
}, },
def: fullContent, def: fullContent,
}), }),
renderSwitchItem('灰色模式', { renderSwitchItem('灰色模式', {
handler: (e) => { handler: (e) => {
baseHandler('grayMode', e); baseHandler(HandlerEnum.GRAY_MODE, e);
}, },
def: grayMode, def: grayMode,
}), }),
renderSwitchItem('色弱模式', { renderSwitchItem('色弱模式', {
handler: (e) => { handler: (e) => {
baseHandler('colorWeak', e); baseHandler(HandlerEnum.COLOR_WEAK, e);
}, },
def: colorWeak, 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) { function renderSelectItem(text: string, config?: SelectConfig) {
const { handler, def, disabled = false, options } = config || {}; const { handler, def, disabled = false, options } = config || {};
@ -693,6 +446,7 @@ export default defineComponent({
</div> </div>
); );
} }
return () => ( return () => (
<BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer"> <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
{{ {{

View 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,
};
});

View 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 {};
}
}

View File

@ -74,6 +74,8 @@ const setting: ProjectConfig = {
collapsedShowSearch: false, collapsedShowSearch: false,
// 折叠触发器的位置 // 折叠触发器的位置
trigger: TriggerEnum.HEADER, trigger: TriggerEnum.HEADER,
// 开启手风琴模式,只显示一个菜单
accordion: true,
}, },
// 消息配置 // 消息配置
messageSetting: { messageSetting: {

View File

@ -24,6 +24,7 @@ export interface MenuSetting {
topMenuAlign: 'start' | 'center' | 'end'; topMenuAlign: 'start' | 'center' | 'end';
collapsedShowSearch: boolean; collapsedShowSearch: boolean;
trigger: TriggerEnum; trigger: TriggerEnum;
accordion: boolean;
} }
export interface MultiTabsSetting { export interface MultiTabsSetting {

14
src/types/global.d.ts vendored
View File

@ -30,12 +30,16 @@ declare type Indexable<T = any> = {
declare type Hash<T> = Indexable<T>; 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> = { declare type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] [P in keyof T]?: DeepPartial<T[P]>;
? RecursivePartial<U>[]
: T[P] extends object
? RecursivePartial<T[P]>
: T[P];
}; };
declare type SelectOptions = { declare type SelectOptions = {

View File

@ -42,7 +42,7 @@ export function useThemeMode(mode: ThemeModeEnum) {
}; };
} }
// 初始化项目配置 // Initial project configuration
export function useInitAppConfigStore() { export function useInitAppConfigStore() {
let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig; let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig;
if (!projCfg) { if (!projCfg) {
@ -78,12 +78,12 @@ export function useConfigProvider() {
}; };
} }
// 初始化网络监听 // Initialize network monitoring
export function useListenerNetWork() { export function useListenerNetWork() {
const { listenNetWork } = appStore.getProjectConfig; const { listenNetWork } = appStore.getProjectConfig;
if (!listenNetWork) return; if (!listenNetWork) return;
const { replace } = useRouter(); const { replace } = useRouter();
// 检测网络状态 // Check network status
useNetWork({ useNetWork({
onLineFn: () => { onLineFn: () => {
replace(PageEnum.BASE_HOME); replace(PageEnum.BASE_HOME);

View File

@ -1,42 +1,42 @@
<template> <template>
<div class="analysis p-4"> <div class="analysis p-4">
<Row class="pl-2"> <a-row class="pl-2">
<template v-for="item in growCardList" :key="item.title"> <template v-for="item in growCardList" :key="item.title">
<ACol :sm="24" :md="12" :lg="6"> <ACol :sm="24" :md="12" :lg="6">
<GrowCard :info="item" /> <GrowCard :info="item" />
</ACol> </ACol>
</template> </template>
</Row> </a-row>
<Row> <a-row>
<ACol :md="24" :lg="17" class="my-3"> <a-col :md="24" :lg="17" class="my-3">
<CollapseContainer class="mr-3" title="产品成交额" :canExpan="false"> <CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
<AnalysisLine /> <AnalysisLine />
</CollapseContainer> </CollapseContainer>
<Row class="mt-3"> <a-row class="mt-3">
<ACol :md="24" :lg="12" class="product-total"> <a-col :md="24" :lg="12" class="product-total">
<CollapseContainer class="mr-3" title="产品成交额" :canExpan="false"> <CollapseContainer class="mr-3" title="产品成交额" :canExpan="false">
<AnalysisPie /> <AnalysisPie />
</CollapseContainer> </CollapseContainer>
</ACol> </a-col>
<ACol :md="24" :lg="12"> <a-col :md="24" :lg="12">
<CollapseContainer class="mr-3" title="用户来源" :canExpan="false"> <CollapseContainer class="mr-3" title="用户来源" :canExpan="false">
<AnalysisBar /> <AnalysisBar />
</CollapseContainer> </CollapseContainer>
</ACol> </a-col>
</Row> </a-row>
</ACol> </a-col>
<ACol :md="24" :lg="7"> <a-col :md="24" :lg="7">
<CollapseContainer class="mt-3" title="项目进度" :canExpan="false"> <CollapseContainer class="mt-3" title="项目进度" :canExpan="false">
<template v-for="item in taskList" :key="item.title"> <template v-for="item in taskList" :key="item.title">
<TaskCard :info="item" /> <TaskCard :info="item" />
</template> </template>
</CollapseContainer> </CollapseContainer>
</ACol> </a-col>
</Row> </a-row>
<Row> <a-row>
<FlowAnalysis /> <FlowAnalysis />
</Row> </a-row>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -48,14 +48,11 @@
import AnalysisBar from './components/AnalysisBar.vue'; import AnalysisBar from './components/AnalysisBar.vue';
import TaskCard from './components/TaskCard.vue'; import TaskCard from './components/TaskCard.vue';
import FlowAnalysis from './components/FlowAnalysis'; import FlowAnalysis from './components/FlowAnalysis';
import { Row, Col } from 'ant-design-vue';
import { CollapseContainer } from '/@/components/Container/index'; import { CollapseContainer } from '/@/components/Container/index';
import { growCardList, taskList } from './data'; import { growCardList, taskList } from './data';
export default defineComponent({ export default defineComponent({
components: { components: {
Row,
ACol: Col,
GrowCard, GrowCard,
CollapseContainer, CollapseContainer,
TrendLine, TrendLine,

View File

@ -1,20 +1,19 @@
<template> <template>
<Row class="workbench p-4" :gutter="12"> <a-row class="workbench p-4" :gutter="12">
<Col :md="24" :lg="17"> <a-col :md="24" :lg="17">
<ProdTotal class="mb-3" /> <ProdTotal class="mb-3" />
<TodoList class="mb-3" /> <TodoList class="mb-3" />
<NewsList class="mb-3" /> <NewsList class="mb-3" />
</Col> </a-col>
<Col :md="24" :lg="7"> <a-col :md="24" :lg="7">
<img src="/@/assets/images/dashboard/wokb/wokb.png" class="workbench__wokb-img mb-3" /> <img src="/@/assets/images/dashboard/wokb/wokb.png" class="workbench__wokb-img mb-3" />
<ShortCuts class="mb-3" /> <ShortCuts class="mb-3" />
<Week class="mb-3" /> <Week class="mb-3" />
</Col> </a-col>
</Row> </a-row>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Row, Col } from 'ant-design-vue';
import ProdTotal from './components/ProdTotal.vue'; import ProdTotal from './components/ProdTotal.vue';
import TodoList from './components/TodoList.vue'; import TodoList from './components/TodoList.vue';
import Week from './components/Week.vue'; import Week from './components/Week.vue';
@ -22,7 +21,7 @@
import ShortCuts from './components/ShortCuts.vue'; import ShortCuts from './components/ShortCuts.vue';
export default defineComponent({ export default defineComponent({
components: { Row, Col, ProdTotal, TodoList, Week, ShortCuts, NewsList }, components: { ProdTotal, TodoList, Week, ShortCuts, NewsList },
setup() { setup() {
return {}; return {};
}, },

View File

@ -168,7 +168,7 @@
display: none; display: none;
height: 100%; height: 100%;
background: url(../../../assets/images/login/login-in.png) no-repeat; background: url(../../../assets/images/login/login-in.png) no-repeat;
background-position: 50% 30%; background-position: 30% 30%;
background-size: 80% 80%; background-size: 80% 80%;
.respond-to(xlarge, { display: block;}); .respond-to(xlarge, { display: block;});
@ -194,9 +194,9 @@
align-items: center; align-items: center;
.respond-to(large, { .respond-to(large, {
width: 600px; 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 { &__content {

View File

@ -34,7 +34,7 @@ const viteConfig: UserConfig = {
* @default 'index.html' * @default 'index.html'
*/ */
// TODO build error // TODO build error
// entry: './public/index.html', // entry: 'public/index.html',
/** /**
* port * port
* @default '3000' * @default '3000'