项目初始化

This commit is contained in:
2026-03-19 10:57:24 +08:00
commit ee94d420ad
3822 changed files with 582614 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
<script lang="tsx">
import type { PropType, CSSProperties } from 'vue';
import { computed, defineComponent, unref, toRef } from 'vue';
import { BasicMenu } from '@jeesite/core/components/Menu';
import { SimpleMenu } from '@jeesite/core/components/SimpleMenu';
import { AppLogo } from '@jeesite/core/components/Application';
import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '@jeesite/core/enums/menuEnum';
import { useMenuSetting } from '@jeesite/core/hooks/setting/useMenuSetting';
import { ScrollContainer } from '@jeesite/core/components/Container';
import { useGo } from '@jeesite/core/hooks/web/usePage';
import { useSplitMenu } from './useLayoutMenu';
import { openWindow } from '@jeesite/core/utils';
import { propTypes } from '@jeesite/core/utils/propTypes';
import { isUrl } from '@jeesite/core/utils/is';
import { useRootSetting } from '@jeesite/core/hooks/setting/useRootSetting';
import { useAppInject } from '@jeesite/core/hooks/web/useAppInject';
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
import { UserDropDown } from '../header/components';
export default defineComponent({
name: 'LayoutMenu',
props: {
theme: propTypes.oneOf(['light', 'dark']),
splitType: {
type: Number as PropType<MenuSplitTyeEnum>,
default: MenuSplitTyeEnum.NONE,
},
isHorizontal: propTypes.bool,
// menu Mode
menuMode: {
type: [String] as PropType<Nullable<MenuModeEnum>>,
default: '',
},
},
setup(props) {
const go = useGo();
const {
getMenuMode,
getMenuType,
getMenuTheme,
getCollapsed,
getCollapsedShowTitle,
getAccordion,
getIsHorizontal,
getIsSidebarType,
getSplit,
} = useMenuSetting();
const { getShowLogo } = useRootSetting();
const { prefixCls } = useDesign('layout-menu');
const { menusRef } = useSplitMenu(toRef(props, 'splitType'));
const { getIsMobile } = useAppInject();
const getComputedMenuMode = computed(() =>
unref(getIsMobile) ? MenuModeEnum.INLINE : props.menuMode || unref(getMenuMode),
);
const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
const getIsShowLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
const getUseScroll = computed(() => {
return (
!unref(getIsHorizontal) &&
(unref(getIsSidebarType) ||
props.splitType === MenuSplitTyeEnum.LEFT ||
props.splitType === MenuSplitTyeEnum.NONE)
);
});
const getWrapperStyle = computed((): CSSProperties => {
return {
height: `calc(100% - ${unref(getIsShowLogo) ? '48px' : '0px'})`,
};
});
const getLogoClass = computed(() => {
return [
`${prefixCls}-logo`,
unref(getComputedMenuTheme),
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
const getCommonProps = computed(() => {
const menus = unref(menusRef);
return {
menus,
beforeClickFn: beforeMenuClickFn,
items: menus,
theme: unref(getComputedMenuTheme),
accordion: unref(getAccordion),
collapse: unref(getCollapsed),
collapsedShowTitle: unref(getCollapsedShowTitle),
onMenuClick: handleMenuClick,
};
});
/**
* click menu
* @param menu
*/
function handleMenuClick(path: string, item: any) {
if (item.target === '_blank') {
window.open(path);
} else {
// const url = String(item.url);
// const paramIdx = url.indexOf('?');
// if (paramIdx != -1 && !item.meta.frameSrc) {
// const params = url.substring(paramIdx);
// go(item.path + params);
// } else {
go(path);
// }
}
}
/**
* before click menu
* @param menu
*/
async function beforeMenuClickFn(path: string) {
if (!isUrl(path)) {
return true;
}
openWindow(path);
return false;
}
function renderHeader() {
if (!unref(getIsShowLogo) && !unref(getIsMobile)) return null;
return (
<AppLogo showTitle={!unref(getCollapsed)} class={unref(getLogoClass)} theme={unref(getComputedMenuTheme)} />
);
}
function renderUserInfo() {
if (unref(getMenuType) === MenuTypeEnum.SIDEBAR) return null;
return <UserDropDown theme={unref(getMenuTheme)} sidebar={true} />;
}
function renderMenu() {
const { menus, ...menuProps } = unref(getCommonProps);
// console.log(menus);
if (!menus || !menus.length) return null;
return !props.isHorizontal ? (
<SimpleMenu
{...menuProps}
isSplitMenu={unref(getSplit)}
items={menus}
v-slots={{
menuBefore: () => renderUserInfo(),
}}
/>
) : (
<BasicMenu
{...(menuProps as any)}
isHorizontal={props.isHorizontal}
type={unref(getMenuType)}
showLogo={unref(getIsShowLogo)}
mode={unref(getComputedMenuMode as any)}
items={menus}
/>
);
}
return () => {
return (
<>
{renderHeader()}
{unref(getUseScroll) ? (
<ScrollContainer style={unref(getWrapperStyle)}>{() => renderMenu()}</ScrollContainer>
) : (
renderMenu()
)}
</>
);
};
},
});
</script>
<style lang="less">
@prefix-cls: ~'jeesite-layout-menu';
@logo-prefix-cls: ~'jeesite-app-logo';
.@{prefix-cls} {
&-logo {
height: @header-height;
padding: 10px 4px 10px 10px;
img {
width: @logo-width;
height: @logo-width;
}
}
&--mobile {
.@{logo-prefix-cls} {
&__title {
opacity: 1;
}
}
}
}
</style>

View File

@@ -0,0 +1,112 @@
import type { Menu } from '@jeesite/core/router/types';
import type { Ref } from 'vue';
import { watch, unref, ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { MenuSplitTyeEnum } from '@jeesite/core/enums/menuEnum';
import { useThrottleFn } from '@vueuse/core';
import { useMenuSetting } from '@jeesite/core/hooks/setting/useMenuSetting';
import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '@jeesite/core/router/menus';
import { usePermissionStore } from '@jeesite/core/store/modules/permission';
import { useAppInject } from '@jeesite/core/hooks/web/useAppInject';
export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
// Menu array
const menusRef = ref<Menu[]>([]);
const { currentRoute } = useRouter();
const { getIsMobile } = useAppInject();
const permissionStore = usePermissionStore();
const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
const throttleHandleSplitLeftMenu = useThrottleFn(handleSplitLeftMenu, 50);
const getSplitNotLeft = computed(() => unref(splitType) !== MenuSplitTyeEnum.LEFT && unref(getSplit));
const getSplitLeft = computed(() => !unref(getSplit) || unref(splitType) !== MenuSplitTyeEnum.LEFT);
const getSpiltTop = computed(() => unref(splitType) === MenuSplitTyeEnum.TOP);
const getNormalType = computed(() => {
return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
});
watch(
[() => unref(currentRoute).path, () => unref(splitType)],
async ([path]: [string, MenuSplitTyeEnum]) => {
if (unref(getSplitNotLeft) || unref(getIsMobile)) return;
const { meta } = unref(currentRoute);
const currentPath = (meta.currentActiveMenu as string) || path;
let parentPath: string | null = await getCurrentParentPath(currentPath);
// if (parentPath) {
// sessionStorage.setItem('temp-parent-path', parentPath);
// } else {
// parentPath = sessionStorage.getItem('temp-parent-path');
// }
if (!parentPath) {
const menus = await getMenus();
parentPath = menus[0] && menus[0].path;
}
// console.log('parentPath', parentPath, path, currentActiveMenu);
parentPath && throttleHandleSplitLeftMenu(parentPath);
},
{
immediate: true,
},
);
// Menu changes
watch(
[() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
() => {
genMenus();
},
{
immediate: true,
},
);
// split Menu changes
watch(
() => getSplit.value,
() => {
if (unref(getSplitNotLeft)) return;
genMenus();
},
);
// Handle left menu split
async function handleSplitLeftMenu(parentPath: string) {
if (unref(getSplitLeft) || unref(getIsMobile)) return;
// spilt mode left
const children = await getChildrenMenus(parentPath);
if (!children || !children.length) {
setMenuSetting({ hidden: true });
menusRef.value = [];
return;
}
setMenuSetting({ hidden: false });
menusRef.value = children;
}
// get menus
async function genMenus() {
// normal mode
if (unref(getNormalType) || unref(getIsMobile)) {
menusRef.value = await getMenus();
return;
}
// split-top
if (unref(getSpiltTop)) {
const shallowMenus = await getShallowMenus();
menusRef.value = shallowMenus;
return;
}
}
return { menusRef };
}