mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 16:15:19 +08:00
wip: refactor layout
This commit is contained in:
@@ -1,214 +0,0 @@
|
||||
import { computed, defineComponent, nextTick, onMounted, ref, unref } from 'vue';
|
||||
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import LayoutTrigger from './LayoutTrigger';
|
||||
import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
|
||||
|
||||
import { menuStore } from '/@/store/modules/menu';
|
||||
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';
|
||||
|
||||
import { useDebounce } from '/@/hooks/core/useDebounce';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutSideBar',
|
||||
setup() {
|
||||
const initRef = ref(false);
|
||||
const brokenRef = ref(false);
|
||||
const collapseRef = ref(true);
|
||||
const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const sideRef = ref<any>(null);
|
||||
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
});
|
||||
|
||||
const getMiniWidth = computed(() => {
|
||||
const {
|
||||
menuSetting: { collapsedShowTitle },
|
||||
} = unref(getProjectConfigRef);
|
||||
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
|
||||
});
|
||||
|
||||
function onCollapseChange(val: boolean) {
|
||||
if (initRef.value) {
|
||||
collapseRef.value = val;
|
||||
menuStore.commitCollapsedState(val);
|
||||
} else {
|
||||
const collapsed = appStore.getProjectConfig.menuSetting.collapsed;
|
||||
!collapsed && menuStore.commitCollapsedState(val);
|
||||
}
|
||||
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);
|
||||
innerE = innerE || window.event;
|
||||
// let tarnameb = innerE.target || innerE.srcElement;
|
||||
const maxT = 600;
|
||||
const minT = unref(getMiniWidth);
|
||||
iT < 0 && (iT = 0);
|
||||
iT > maxT && (iT = maxT);
|
||||
iT < minT && (iT = minT);
|
||||
ele.style.left = wrap.style.width = iT + 'px';
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// 菜单区域拖拽 - 鼠标松开
|
||||
function removeMouseup(ele: any) {
|
||||
const wrap = unref(sideRef).$el;
|
||||
document.onmouseup = function () {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
const width = parseInt(wrap.style.width);
|
||||
menuStore.commitDragStartState(false);
|
||||
if (!menuStore.getCollapsedState) {
|
||||
if (width > unref(getMiniWidth) + 20) {
|
||||
setMenuWidth(width);
|
||||
} else {
|
||||
menuStore.commitCollapsedState(true);
|
||||
}
|
||||
} else {
|
||||
if (width > unref(getMiniWidth)) {
|
||||
setMenuWidth(width);
|
||||
menuStore.commitCollapsedState(false);
|
||||
}
|
||||
}
|
||||
|
||||
ele.releaseCapture && ele.releaseCapture();
|
||||
};
|
||||
}
|
||||
|
||||
function setMenuWidth(width: number) {
|
||||
appStore.commitProjectConfigState({
|
||||
menuSetting: {
|
||||
menuWidth: width,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeWrapWidth() {
|
||||
const ele = unref(dragBarRef) as any;
|
||||
const side = unref(sideRef);
|
||||
|
||||
const wrap = (side || {}).$el;
|
||||
ele &&
|
||||
(ele.onmousedown = (e: any) => {
|
||||
menuStore.commitDragStartState(true);
|
||||
wrap.style.transition = 'unset';
|
||||
const clientX = (e || event).clientX;
|
||||
ele.left = ele.offsetLeft;
|
||||
handleMouseMove(ele, wrap, clientX);
|
||||
removeMouseup(ele);
|
||||
ele.setCapture && ele.setCapture();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
function handleBreakpoint(broken: boolean) {
|
||||
brokenRef.value = broken;
|
||||
}
|
||||
|
||||
const getDragBarStyle = computed(() => {
|
||||
if (menuStore.getCollapsedState) {
|
||||
return { left: `${unref(getMiniWidth)}px` };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const getCollapsedWidth = computed(() => {
|
||||
return unref(brokenRef) ? 0 : unref(getMiniWidth);
|
||||
});
|
||||
|
||||
const showTrigger = computed(() => {
|
||||
const {
|
||||
menuSetting: { trigger },
|
||||
} = unref(getProjectConfigRef);
|
||||
return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const [exec] = useDebounce(changeWrapWidth, 20);
|
||||
exec();
|
||||
});
|
||||
});
|
||||
|
||||
function handleSiderClick(e: ChangeEvent) {
|
||||
if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
|
||||
|
||||
const { collapsed, show } = appStore.getProjectConfig.menuSetting;
|
||||
if (!collapsed || !show) return;
|
||||
appStore.commitProjectConfigState({
|
||||
menuSetting: {
|
||||
collapsed: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function renderDragLine() {
|
||||
const { menuSetting: { hasDrag = true } = {} } = unref(getProjectConfigRef);
|
||||
return (
|
||||
<div
|
||||
class={[`layout-sidebar__dargbar`, !hasDrag ? 'hide' : '']}
|
||||
style={unref(getDragBarStyle)}
|
||||
ref={dragBarRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
const {
|
||||
menuSetting: { theme, split: splitMenu },
|
||||
} = unref(getProjectConfigRef);
|
||||
const { getCollapsedState, getMenuWidthState } = menuStore;
|
||||
|
||||
const triggerDom = unref(showTrigger)
|
||||
? {
|
||||
trigger: () => <LayoutTrigger />,
|
||||
}
|
||||
: {};
|
||||
|
||||
const triggerAttr = unref(showTrigger)
|
||||
? {}
|
||||
: {
|
||||
trigger: null,
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout.Sider
|
||||
onClick={handleSiderClick}
|
||||
onCollapse={onCollapseChange}
|
||||
breakpoint="md"
|
||||
width={getMenuWidthState}
|
||||
collapsed={getCollapsedState}
|
||||
collapsible
|
||||
collapsedWidth={unref(getCollapsedWidth)}
|
||||
theme={theme}
|
||||
class="layout-sidebar"
|
||||
ref={sideRef}
|
||||
onBreakpoint={handleBreakpoint}
|
||||
{...triggerAttr}
|
||||
>
|
||||
{{
|
||||
...triggerDom,
|
||||
default: () => (
|
||||
<>
|
||||
<LayoutMenu
|
||||
theme={theme}
|
||||
menuMode={splitMenu ? MenuModeEnum.INLINE : null}
|
||||
splitType={splitMenu ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE}
|
||||
/>
|
||||
{renderDragLine()}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
</Layout.Sider>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -1,41 +1,39 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, unref } from 'vue';
|
||||
import {
|
||||
DoubleRightOutlined,
|
||||
DoubleLeftOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
MenuFoldOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
// store
|
||||
import { menuStore } from '/@/store/modules/menu';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutTrigger',
|
||||
props: {
|
||||
sider: {
|
||||
type: Boolean,
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
function toggleMenu() {
|
||||
menuStore.commitCollapsedState(!menuStore.getCollapsedState);
|
||||
}
|
||||
const { toggleCollapsed, getCollapsed } = useMenuSetting();
|
||||
|
||||
return () => {
|
||||
const siderTrigger = menuStore.getCollapsedState ? (
|
||||
<DoubleRightOutlined />
|
||||
) : (
|
||||
<DoubleLeftOutlined />
|
||||
);
|
||||
if (props.sider) return siderTrigger;
|
||||
const siderTrigger = unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
|
||||
|
||||
if (props.sider) {
|
||||
return siderTrigger;
|
||||
}
|
||||
|
||||
return (
|
||||
<span class={['layout-trigger', props.theme]} onClick={toggleMenu}>
|
||||
{menuStore.getCollapsedState ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
<span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
|
||||
{unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@@ -4,14 +4,17 @@ import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue';
|
||||
import Breadcrumb from '/@/components/Breadcrumb/Breadcrumb.vue';
|
||||
import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import router from '/@/router';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { isBoolean } from '/@/utils/is';
|
||||
|
||||
import { compile } from 'path-to-regexp';
|
||||
import Icon from '/@/components/Icon';
|
||||
import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { isBoolean } from '/@/utils/is';
|
||||
import { compile } from 'path-to-regexp';
|
||||
|
||||
import router from '/@/router';
|
||||
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicBreadcrumb',
|
||||
@@ -40,7 +43,6 @@ export default defineComponent({
|
||||
const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1);
|
||||
const firstItem = matchedList[0];
|
||||
const ret = getHomeRoute(firstItem);
|
||||
|
||||
if (!isBoolean(ret)) {
|
||||
matchedList.unshift(ret);
|
||||
}
|
||||
@@ -74,42 +76,51 @@ export default defineComponent({
|
||||
return push(pathCompile(path));
|
||||
}
|
||||
|
||||
function renderItemContent(item: AppRouteRecordRaw) {
|
||||
return (
|
||||
<>
|
||||
{props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (
|
||||
<Icon
|
||||
icon={item.meta.icon}
|
||||
class="icon mr-1 "
|
||||
style={{
|
||||
marginBottom: '2px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{item.meta.title}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderBreadcrumbItemList() {
|
||||
return unref(itemList).map((item) => {
|
||||
const isLink =
|
||||
(!!item.redirect && !item.meta.disabledRedirect) ||
|
||||
!item.children ||
|
||||
item.children.length === 0;
|
||||
|
||||
return (
|
||||
<BreadcrumbItem
|
||||
key={item.path}
|
||||
isLink={isLink}
|
||||
onClick={handleItemClick.bind(null, item)}
|
||||
>
|
||||
{() => renderItemContent(item as AppRouteRecordRaw)}
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function renderBreadcrumbDefault() {
|
||||
return (
|
||||
<TransitionGroup name="breadcrumb">{() => renderBreadcrumbItemList()}</TransitionGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return () => (
|
||||
<Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}>
|
||||
{() => (
|
||||
<TransitionGroup name="breadcrumb">
|
||||
{() => {
|
||||
return unref(itemList).map((item) => {
|
||||
const isLink =
|
||||
(!!item.redirect && !item.meta.disabledRedirect) ||
|
||||
!item.children ||
|
||||
item.children.length === 0;
|
||||
return (
|
||||
<BreadcrumbItem
|
||||
key={item.path}
|
||||
isLink={isLink}
|
||||
onClick={handleItemClick.bind(null, item)}
|
||||
>
|
||||
{() => (
|
||||
<>
|
||||
{props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (
|
||||
<Icon
|
||||
icon={item.meta.icon}
|
||||
class="icon mr-1 "
|
||||
style={{
|
||||
marginBottom: '2px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{item.meta.title}
|
||||
</>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
});
|
||||
}}
|
||||
</TransitionGroup>
|
||||
)}
|
||||
{() => renderBreadcrumbDefault()}
|
||||
</Breadcrumb>
|
||||
);
|
||||
},
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import './index.less';
|
||||
|
||||
import { defineComponent, unref, computed, ref } from 'vue';
|
||||
|
||||
import { Layout, Tooltip, Badge } from 'ant-design-vue';
|
||||
import Logo from '/@/layouts/logo/index.vue';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
import UserDropdown from './UserDropdown';
|
||||
import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
|
||||
import LayoutBreadcrumb from './LayoutBreadcrumb';
|
||||
@@ -12,50 +14,57 @@ import {
|
||||
RedoOutlined,
|
||||
FullscreenExitOutlined,
|
||||
FullscreenOutlined,
|
||||
GithubFilled,
|
||||
LockOutlined,
|
||||
BugOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
|
||||
import { useFullscreen } from '/@/hooks/web/useFullScreen';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { errorStore } from '/@/store/modules/error';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
|
||||
import { GITHUB_URL } from '/@/settings/siteSetting';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
import { Component } from '/@/components/types';
|
||||
|
||||
import './index.less';
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutHeader',
|
||||
name: 'LayoutHeader',
|
||||
setup() {
|
||||
const widthRef = ref(200);
|
||||
let logoEl: Element | null;
|
||||
|
||||
const widthRef = ref(200);
|
||||
|
||||
const { refreshPage } = useTabs();
|
||||
|
||||
const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting();
|
||||
|
||||
const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
|
||||
|
||||
const {
|
||||
getTheme,
|
||||
getShowRedo,
|
||||
getUseLockPage,
|
||||
getShowFullScreen,
|
||||
getShowNotice,
|
||||
getShowContent,
|
||||
getShowBread,
|
||||
getShowHeaderLogo,
|
||||
} = useHeaderSetting();
|
||||
|
||||
const { push } = useRouter();
|
||||
const [register, { openModal }] = useModal();
|
||||
const { toggleFullscreen, isFullscreenRef } = useFullscreen();
|
||||
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
});
|
||||
|
||||
const showTopMenu = computed(() => {
|
||||
const getProjectConfig = unref(getProjectConfigRef);
|
||||
const {
|
||||
menuSetting: { mode, split: splitMenu },
|
||||
} = getProjectConfig;
|
||||
return mode === MenuModeEnum.HORIZONTAL || splitMenu;
|
||||
});
|
||||
|
||||
useWindowSizeFn(
|
||||
() => {
|
||||
if (!unref(showTopMenu)) return;
|
||||
if (!unref(getShowTopMenu)) return;
|
||||
let width = 0;
|
||||
if (!logoEl) {
|
||||
logoEl = document.querySelector('.layout-header__logo');
|
||||
@@ -69,24 +78,23 @@ export default defineComponent({
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function goToGithub() {
|
||||
window.open(GITHUB_URL, '__blank');
|
||||
}
|
||||
|
||||
const headerClass = computed(() => {
|
||||
const theme = unref(getProjectConfigRef).headerSetting.theme;
|
||||
const theme = unref(getTheme);
|
||||
return theme ? `layout-header__header--${theme}` : '';
|
||||
});
|
||||
|
||||
const showHeaderTrigger = computed(() => {
|
||||
const { show, trigger, hidden, type } = unref(getProjectConfigRef).menuSetting;
|
||||
if (type === MenuTypeEnum.TOP_MENU || !show || !hidden) return false;
|
||||
return trigger === TriggerEnum.HEADER;
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
const getMenuMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
|
||||
});
|
||||
|
||||
function handleToErrorList() {
|
||||
errorStore.commitErrorListCountState(0);
|
||||
push('/exception/error-log');
|
||||
push(PageEnum.ERROR_LOG_PAGE).then(() => {
|
||||
errorStore.commitErrorListCountState(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,162 +104,129 @@ export default defineComponent({
|
||||
openModal(true);
|
||||
}
|
||||
|
||||
return () => {
|
||||
const getProjectConfig = unref(getProjectConfigRef);
|
||||
const {
|
||||
useErrorHandle,
|
||||
showLogo,
|
||||
multiTabsSetting: { show: showTab },
|
||||
headerSetting: {
|
||||
theme: headerTheme,
|
||||
useLockPage,
|
||||
showRedo,
|
||||
showGithub,
|
||||
showFullScreen,
|
||||
showNotice,
|
||||
},
|
||||
menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign },
|
||||
showBreadCrumb,
|
||||
showBreadCrumbIcon,
|
||||
} = getProjectConfig;
|
||||
|
||||
const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
|
||||
|
||||
function renderHeaderContent() {
|
||||
const width = unref(widthRef);
|
||||
return (
|
||||
<div class="layout-header__content ">
|
||||
{unref(getShowHeaderLogo) && (
|
||||
<AppLogo class={`layout-header__logo`} theme={unref(getTheme)} />
|
||||
)}
|
||||
|
||||
const showLeft =
|
||||
(mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu) ||
|
||||
unref(showHeaderTrigger);
|
||||
{unref(getShowContent) && (
|
||||
<div class="layout-header__left">
|
||||
{unref(getShowHeaderTrigger) && (
|
||||
<LayoutTrigger theme={unref(getTheme)} sider={false} />
|
||||
)}
|
||||
{unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{unref(getShowTopMenu) && (
|
||||
<div class={[`layout-header__menu `]} style={{ width: `calc(100% - ${width}px)` }}>
|
||||
<LayoutMenu
|
||||
isHorizontal={true}
|
||||
class={`justify-${unref(getTopMenuAlign)}`}
|
||||
theme={unref(getTheme)}
|
||||
splitType={unref(getSplitType)}
|
||||
menuMode={unref(getMenuMode)}
|
||||
showSearch={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderActionDefault(Comp: Component | any, event: Fn) {
|
||||
return (
|
||||
<div class={`layout-header__action-item`} onClick={event}>
|
||||
<Comp class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderAction() {
|
||||
return (
|
||||
<div class={`layout-header__action`}>
|
||||
{unref(getUseErrorHandle) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '错误日志',
|
||||
default: () => (
|
||||
<Badge
|
||||
count={errorStore.getErrorListCountState}
|
||||
offset={[0, 10]}
|
||||
dot
|
||||
overflowCount={99}
|
||||
>
|
||||
{() => renderActionDefault(BugOutlined, handleToErrorList)}
|
||||
</Badge>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{unref(getUseLockPage) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '锁定屏幕',
|
||||
default: () => renderActionDefault(LockOutlined, handleLockPage),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{unref(getShowNotice) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '消息通知',
|
||||
default: () => <NoticeAction />,
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{unref(getShowRedo) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '刷新',
|
||||
default: () => renderActionDefault(RedoOutlined, refreshPage),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{unref(getShowFullScreen) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
|
||||
default: () => {
|
||||
const Icon = !unref(isFullscreenRef) ? (
|
||||
<FullscreenOutlined />
|
||||
) : (
|
||||
<FullscreenExitOutlined />
|
||||
);
|
||||
return renderActionDefault(Icon, toggleFullscreen);
|
||||
},
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
<UserDropdown class={`layout-header__user-dropdown`} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderHeaderDefault() {
|
||||
return (
|
||||
<>
|
||||
{renderHeaderContent()}
|
||||
{renderAction()}
|
||||
<LockAction onRegister={register} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
|
||||
{() => (
|
||||
<>
|
||||
<div class="layout-header__content ">
|
||||
{showLogo && !isSidebarType && (
|
||||
<Logo class={`layout-header__logo`} theme={headerTheme} />
|
||||
)}
|
||||
|
||||
{showLeft && (
|
||||
<div class="layout-header__left">
|
||||
{unref(showHeaderTrigger) && (
|
||||
<LayoutTrigger theme={headerTheme} sider={false} />
|
||||
)}
|
||||
{mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu && (
|
||||
<LayoutBreadcrumb showIcon={showBreadCrumbIcon} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{unref(showTopMenu) && (
|
||||
<div
|
||||
class={[`layout-header__menu `]}
|
||||
style={{ width: `calc(100% - ${unref(width)}px)` }}
|
||||
>
|
||||
<LayoutMenu
|
||||
isTop={true}
|
||||
class={`justify-${topMenuAlign}`}
|
||||
theme={headerTheme}
|
||||
splitType={splitMenu ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE}
|
||||
menuMode={splitMenu ? MenuModeEnum.HORIZONTAL : null}
|
||||
showSearch={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class={`layout-header__action`}>
|
||||
{useErrorHandle && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '错误日志',
|
||||
default: () => (
|
||||
<Badge
|
||||
count={errorStore.getErrorListCountState}
|
||||
offset={[0, 10]}
|
||||
dot
|
||||
overflowCount={99}
|
||||
>
|
||||
{() => (
|
||||
<div class={`layout-header__action-item`} onClick={handleToErrorList}>
|
||||
<BugOutlined class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
)}
|
||||
</Badge>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{showGithub && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => 'github',
|
||||
default: () => (
|
||||
<div class={`layout-header__action-item`} onClick={goToGithub}>
|
||||
<GithubFilled class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
{useLockPage && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '锁定屏幕',
|
||||
default: () => (
|
||||
<div class={`layout-header__action-item`} onClick={handleLockPage}>
|
||||
<LockOutlined class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
{showNotice && (
|
||||
<div>
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '消息通知',
|
||||
default: () => <NoticeAction />,
|
||||
}}
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
{showRedo && showTab && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '刷新',
|
||||
default: () => (
|
||||
<div class={`layout-header__action-item`} onClick={refreshPage}>
|
||||
<RedoOutlined class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
{showFullScreen && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
|
||||
default: () => {
|
||||
const Icon: any = !unref(isFullscreenRef) ? (
|
||||
<FullscreenOutlined />
|
||||
) : (
|
||||
<FullscreenExitOutlined />
|
||||
);
|
||||
return (
|
||||
<div class={`layout-header__action-item`} onClick={toggleFullscreen}>
|
||||
<Icon class={`layout-header__action-icon`} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}}
|
||||
</Tooltip>
|
||||
)}
|
||||
<UserDropdown class={`layout-header__user-dropdown`} />
|
||||
</div>
|
||||
<LockAction onRegister={register} />
|
||||
</>
|
||||
)}
|
||||
{() => renderHeaderDefault()}
|
||||
</Layout.Header>
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,6 @@
|
||||
.lock-modal {
|
||||
&__entry {
|
||||
position: relative;
|
||||
// width: 500px;
|
||||
height: 240px;
|
||||
padding: 130px 30px 60px 30px;
|
||||
background: #fff;
|
||||
|
@@ -1,41 +1,33 @@
|
||||
// 组件相关
|
||||
import './LockActionItem.less';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal/index';
|
||||
|
||||
// hook
|
||||
import Button from '/@/components/Button/index.vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
|
||||
import headerImg from '/@/assets/images/header.jpg';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { userStore } from '/@/store/modules/user';
|
||||
import Button from '/@/components/Button/index.vue';
|
||||
import './LockActionItem.less';
|
||||
|
||||
const prefixCls = 'lock-modal';
|
||||
export default defineComponent({
|
||||
name: 'LockModal',
|
||||
setup(_, { attrs }) {
|
||||
const [register, { setModalProps }] = useModalInner();
|
||||
// 样式前缀
|
||||
const [register, { closeModal }] = useModalInner();
|
||||
|
||||
const [registerForm, { validateFields, resetFields }] = useForm({
|
||||
// 隐藏按钮
|
||||
showActionButtonGroup: false,
|
||||
// 表单项
|
||||
schemas: [
|
||||
{
|
||||
field: 'password',
|
||||
label: '',
|
||||
label: '锁屏密码',
|
||||
component: 'InputPassword',
|
||||
componentProps: {
|
||||
placeholder: '请输入锁屏密码',
|
||||
},
|
||||
rules: [{ required: true }],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
/**
|
||||
* @description: lock
|
||||
*/
|
||||
|
||||
async function lock(valid = true) {
|
||||
let password: string | undefined = '';
|
||||
|
||||
@@ -46,9 +38,7 @@ export default defineComponent({
|
||||
const values = (await validateFields()) as any;
|
||||
password = values.password;
|
||||
}
|
||||
setModalProps({
|
||||
visible: false,
|
||||
});
|
||||
closeModal();
|
||||
|
||||
appStore.commitLockInfoState({
|
||||
isLock: true,
|
||||
@@ -57,7 +47,7 @@ export default defineComponent({
|
||||
await resetFields();
|
||||
} catch (error) {}
|
||||
}
|
||||
// 账号密码登录
|
||||
|
||||
return () => (
|
||||
<BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}>
|
||||
{() => (
|
||||
@@ -66,7 +56,9 @@ export default defineComponent({
|
||||
<img src={headerImg} class={`${prefixCls}__header-img`} />
|
||||
<p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p>
|
||||
</div>
|
||||
<BasicForm onRegister={registerForm} />
|
||||
|
||||
<BasicForm onRegister={registerForm} layout="vertical" />
|
||||
|
||||
<div class={`${prefixCls}__footer`}>
|
||||
<Button type="primary" block class="mt-2" onClick={lock}>
|
||||
{() => '锁屏'}
|
||||
|
@@ -11,15 +11,23 @@ import Icon from '/@/components/Icon/index';
|
||||
import { userStore } from '/@/store/modules/user';
|
||||
|
||||
import { DOC_URL } from '/@/settings/siteSetting';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
|
||||
interface RenderItemParams {
|
||||
icon: string;
|
||||
text: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
const prefixCls = 'user-dropdown';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserDropdown',
|
||||
setup() {
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
});
|
||||
const { getShowDoc } = useHeaderSetting();
|
||||
|
||||
const getUserInfo = computed(() => {
|
||||
const { realName = '', desc } = userStore.getUserInfoState || {};
|
||||
@@ -33,7 +41,7 @@ export default defineComponent({
|
||||
|
||||
// open doc
|
||||
function openDoc() {
|
||||
window.open(DOC_URL, '__blank');
|
||||
openWindow(DOC_URL);
|
||||
}
|
||||
|
||||
function handleMenuClick(e: any) {
|
||||
@@ -44,7 +52,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
function renderItem({ icon, text, key }: { icon: string; text: string; key: string }) {
|
||||
function renderItem({ icon, text, key }: RenderItemParams) {
|
||||
return (
|
||||
<Menu.Item key={key}>
|
||||
{() => (
|
||||
@@ -57,37 +65,43 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
function renderSlotsDefault() {
|
||||
const { realName } = unref(getUserInfo);
|
||||
const {
|
||||
headerSetting: { showDoc },
|
||||
} = unref(getProjectConfigRef);
|
||||
return (
|
||||
<section class={prefixCls}>
|
||||
<img class={`${prefixCls}__header`} src={headerImg} />
|
||||
<section class={`${prefixCls}__info`}>
|
||||
<section class={`${prefixCls}__name`}>{realName}</section>
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSlotOverlay() {
|
||||
const showDoc = unref(getShowDoc);
|
||||
return (
|
||||
<Menu onClick={handleMenuClick}>
|
||||
{() => (
|
||||
<>
|
||||
{showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
|
||||
{showDoc && <Divider />}
|
||||
{renderItem({
|
||||
key: 'loginOut',
|
||||
text: '退出系统',
|
||||
icon: 'ant-design:poweroff-outlined',
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Dropdown placement="bottomLeft">
|
||||
{{
|
||||
default: () => (
|
||||
<section class={prefixCls}>
|
||||
<img class={`${prefixCls}__header`} src={headerImg} />
|
||||
<section class={`${prefixCls}__info`}>
|
||||
<section class={`${prefixCls}__name`}>{realName}</section>
|
||||
</section>
|
||||
</section>
|
||||
),
|
||||
overlay: () => (
|
||||
<Menu slot="overlay" onClick={handleMenuClick}>
|
||||
{() => (
|
||||
<>
|
||||
{showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
|
||||
{showDoc && <Divider />}
|
||||
{renderItem({
|
||||
key: 'loginOut',
|
||||
text: '退出系统',
|
||||
icon: 'ant-design:poweroff-outlined',
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
),
|
||||
default: () => renderSlotsDefault(),
|
||||
overlay: () => renderSlotOverlay(),
|
||||
}}
|
||||
</Dropdown>
|
||||
);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="layout-header__action-item notify-action">
|
||||
<Popover title="" trigger="click">
|
||||
<Popover title="" trigger="click" overlayClassName="layout-header__notify-action">
|
||||
<Badge :count="count" dot :numberStyle="numberStyle">
|
||||
<BellOutlined class="layout-header__action-icon" />
|
||||
</Badge>
|
||||
@@ -31,6 +31,7 @@
|
||||
components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
|
||||
setup() {
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < tabListData.length; i++) {
|
||||
count += tabListData[i].list.length;
|
||||
}
|
||||
@@ -44,6 +45,10 @@
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.layout-header__notify-action {
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.notify-action {
|
||||
padding-top: 2px;
|
||||
|
||||
@@ -56,7 +61,6 @@
|
||||
|
||||
.ant-badge-multiple-words {
|
||||
padding: 0 4px;
|
||||
// transform: translate(26%, -40%);
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@@ -1,36 +1,37 @@
|
||||
<template>
|
||||
<List class="list">
|
||||
<a-list class="list">
|
||||
<template v-for="item in list" :key="item.id">
|
||||
<ListItem class="list__item">
|
||||
<ListItemMeta>
|
||||
<a-list-item class="list-item">
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
{{ item.title }}
|
||||
<div class="extra" v-if="item.extra">
|
||||
<Tag class="tag" :color="item.color">
|
||||
<a-tag class="tag" :color="item.color">
|
||||
{{ item.extra }}
|
||||
</Tag>
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #avatar>
|
||||
<Avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
|
||||
<a-avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
|
||||
<span v-else> {{ item.avatar }}</span>
|
||||
</template>
|
||||
|
||||
<template #description>
|
||||
<div>
|
||||
<div class="description">{{ item.description }}</div>
|
||||
<div class="datetime">{{ item.datetime }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</ListItemMeta>
|
||||
</ListItem>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</List>
|
||||
</a-list>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { List, Avatar, Tag } from 'ant-design-vue';
|
||||
import { ListItem } from './data';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -40,19 +41,6 @@
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
components: {
|
||||
List,
|
||||
ListItem: List.Item,
|
||||
ListItemMeta: List.Item.Meta,
|
||||
Avatar,
|
||||
Tag,
|
||||
},
|
||||
setup(props) {
|
||||
const { list = [] } = props;
|
||||
return {
|
||||
list,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@@ -61,7 +49,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__item {
|
||||
&-item {
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
@@ -56,14 +56,6 @@ export const tabListData: TabItem[] = [
|
||||
datetime: '2017-08-07',
|
||||
type: '1',
|
||||
},
|
||||
// {
|
||||
// id: '000000005',
|
||||
// avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
|
||||
// title: '内容不要超过两行字,超出时自动截断',
|
||||
// description: '',
|
||||
// datetime: '2017-08-07',
|
||||
// type: '1',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@@ -36,47 +36,4 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-sidebar {
|
||||
background-size: 100% 100%;
|
||||
|
||||
&.ant-layout-sider-dark {
|
||||
background: @sider-dark-bg-color;
|
||||
}
|
||||
|
||||
&:not(.ant-layout-sider-dark) {
|
||||
border-right: 1px solid @border-color-light;
|
||||
}
|
||||
|
||||
.ant-layout-sider-zero-width-trigger {
|
||||
top: 40%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&__dargbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2px;
|
||||
z-index: @side-drag-z-index;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: @primary-color;
|
||||
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout-sider-trigger {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import LayoutHeader from './header/LayoutHeader';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import LayoutContent from './LayoutContent';
|
||||
import LayoutSideBar from './LayoutSideBar';
|
||||
import LayoutSideBar from './sider/LayoutSideBar';
|
||||
import SettingBtn from './setting/index.vue';
|
||||
import MultipleTabs from './multitabs/index';
|
||||
|
||||
@@ -36,7 +36,7 @@ export default defineComponent({
|
||||
return show;
|
||||
});
|
||||
|
||||
const isShowMixHeaderRef = computed(() => {
|
||||
const showMixHeaderRef = computed(() => {
|
||||
const {
|
||||
menuSetting: { type },
|
||||
} = unref(getProjectConfigRef);
|
||||
@@ -57,11 +57,11 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const showFullHeaderRef = computed(() => {
|
||||
return !unref(getFullContent) && unref(isShowMixHeaderRef) && unref(showHeaderRef);
|
||||
return !unref(getFullContent) && unref(showMixHeaderRef) && unref(showHeaderRef);
|
||||
});
|
||||
|
||||
const showInsetHeaderRef = computed(() => {
|
||||
return !unref(getFullContent) && !unref(isShowMixHeaderRef) && unref(showHeaderRef);
|
||||
return !unref(getFullContent) && !unref(showMixHeaderRef) && unref(showHeaderRef);
|
||||
});
|
||||
|
||||
const fixedHeaderClsRef = computed(() => {
|
||||
|
@@ -1,29 +1,21 @@
|
||||
import type { PropType } from 'vue';
|
||||
import './index.less';
|
||||
|
||||
import { PropType, toRef } from 'vue';
|
||||
import type { Menu } from '/@/router/types';
|
||||
|
||||
import { computed, defineComponent, unref, ref, onMounted, watch } from 'vue';
|
||||
import { computed, defineComponent, unref } from 'vue';
|
||||
import { BasicMenu } from '/@/components/Menu/index';
|
||||
import Logo from '/@/layouts/logo/index.vue';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
// store
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { menuStore } from '/@/store/modules/menu';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
import {
|
||||
getMenus,
|
||||
getFlatMenus,
|
||||
getShallowMenus,
|
||||
getChildrenMenus,
|
||||
getFlatChildrenMenus,
|
||||
getCurrentParentPath,
|
||||
} from '/@/router/menus/index';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThrottle } from '/@/hooks/core/useThrottle';
|
||||
import { permissionStore } from '/@/store/modules/permission';
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
import { useSplitMenu } from './useLayoutMenu';
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
import './index.less';
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutMenu',
|
||||
props: {
|
||||
@@ -43,7 +35,7 @@ export default defineComponent({
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
isTop: {
|
||||
isHorizontal: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
@@ -53,190 +45,99 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// Menu array
|
||||
const menusRef = ref<Menu[]>([]);
|
||||
// flat menu array
|
||||
const flatMenusRef = ref<Menu[]>([]);
|
||||
const { currentRoute, push } = useRouter();
|
||||
const go = useGo();
|
||||
|
||||
// get app config
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
const {
|
||||
setMenuSetting,
|
||||
getShowSearch,
|
||||
getMode,
|
||||
getType,
|
||||
getCollapsedShowTitle,
|
||||
getCollapsedShowSearch,
|
||||
getIsSidebarType,
|
||||
getTheme,
|
||||
getCollapsed,
|
||||
getAccordion,
|
||||
} = useMenuSetting();
|
||||
|
||||
const { getShowLogo } = useRootSetting();
|
||||
|
||||
const { flatMenusRef, menusRef } = useSplitMenu(toRef(props, 'splitType'));
|
||||
|
||||
const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
|
||||
|
||||
const getMenuMode = computed(() => props.menuMode || unref(getMode));
|
||||
|
||||
const getMenuTheme = computed(() => props.theme || unref(getTheme));
|
||||
|
||||
const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
|
||||
|
||||
const showSearch = computed(() => {
|
||||
return (
|
||||
unref(getShowSearch) &&
|
||||
props.showSearch &&
|
||||
(unref(getCollapsedShowSearch) ? true : !unref(getCollapsed))
|
||||
);
|
||||
});
|
||||
|
||||
// get is Horizontal
|
||||
const getIsHorizontalRef = computed(() => {
|
||||
return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
|
||||
});
|
||||
|
||||
const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
|
||||
|
||||
// Route change split menu
|
||||
watch(
|
||||
[() => unref(currentRoute).path, () => props.splitType],
|
||||
async ([path, splitType]: [string, MenuSplitTyeEnum]) => {
|
||||
if (splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
|
||||
const parentPath = await getCurrentParentPath(path);
|
||||
parentPath && throttleHandleSplitLeftMenu(parentPath);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Menu changes
|
||||
watch(
|
||||
[() => 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;
|
||||
// spilt mode left
|
||||
if (splitType === MenuSplitTyeEnum.LEFT) {
|
||||
const children = await getChildrenMenus(parentPath);
|
||||
if (!children) {
|
||||
appStore.commitProjectConfigState({
|
||||
menuSetting: {
|
||||
hidden: false,
|
||||
},
|
||||
});
|
||||
flatMenusRef.value = [];
|
||||
menusRef.value = [];
|
||||
return;
|
||||
}
|
||||
const flatChildren = await getFlatChildrenMenus(children);
|
||||
appStore.commitProjectConfigState({
|
||||
menuSetting: {
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
flatMenusRef.value = flatChildren;
|
||||
menusRef.value = 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();
|
||||
menusRef.value = await getMenus();
|
||||
return;
|
||||
}
|
||||
|
||||
// split-top
|
||||
if (splitType === MenuSplitTyeEnum.TOP) {
|
||||
const parentPath = await getCurrentParentPath(unref(currentRoute).path);
|
||||
menuStore.commitCurrentTopSplitMenuPathState(parentPath);
|
||||
const shallowMenus = await getShallowMenus();
|
||||
|
||||
flatMenusRef.value = shallowMenus;
|
||||
menusRef.value = shallowMenus;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* click menu
|
||||
* @param menu
|
||||
*/
|
||||
function handleMenuClick(menu: Menu) {
|
||||
const { path } = menu;
|
||||
if (path) {
|
||||
push(path);
|
||||
const { splitType } = props;
|
||||
// split mode top
|
||||
if (splitType === MenuSplitTyeEnum.TOP) {
|
||||
menuStore.commitCurrentTopSplitMenuPathState(path);
|
||||
}
|
||||
}
|
||||
go(menu.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* before click menu
|
||||
* @param menu
|
||||
*/
|
||||
async function beforeMenuClickFn(menu: Menu) {
|
||||
const { meta: { externalLink } = {} } = menu;
|
||||
|
||||
if (externalLink) {
|
||||
window.open(externalLink, '_blank');
|
||||
openWindow(externalLink);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleClickSearchInput() {
|
||||
if (menuStore.getCollapsedState) {
|
||||
menuStore.commitCollapsedState(false);
|
||||
}
|
||||
unref(getCollapsed) && setMenuSetting({ collapsed: false });
|
||||
}
|
||||
|
||||
const showSearchRef = computed(() => {
|
||||
const { showSearch, type, mode } = unref(getProjectConfigRef).menuSetting;
|
||||
function renderHeader() {
|
||||
if (!unref(showLogo)) return null;
|
||||
return (
|
||||
showSearch &&
|
||||
props.showSearch &&
|
||||
!(type === MenuTypeEnum.MIX && mode === MenuModeEnum.HORIZONTAL)
|
||||
<AppLogo
|
||||
showTitle={!unref(getCollapsed)}
|
||||
class={[`layout-menu__logo`, unref(getMenuTheme)]}
|
||||
theme={unref(getMenuTheme)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
genMenus();
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
const {
|
||||
showLogo,
|
||||
menuSetting: {
|
||||
type: menuType,
|
||||
mode,
|
||||
theme,
|
||||
collapsed,
|
||||
collapsedShowTitle,
|
||||
collapsedShowSearch,
|
||||
accordion,
|
||||
},
|
||||
} = unref(getProjectConfigRef);
|
||||
|
||||
const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
|
||||
const isShowLogo = showLogo && isSidebarType;
|
||||
const themeData = props.theme || theme;
|
||||
return (
|
||||
<BasicMenu
|
||||
beforeClickFn={beforeMenuClickFn}
|
||||
onMenuClick={handleMenuClick}
|
||||
type={menuType}
|
||||
mode={props.menuMode || mode}
|
||||
class="layout-menu"
|
||||
collapsedShowTitle={collapsedShowTitle}
|
||||
theme={themeData}
|
||||
showLogo={isShowLogo}
|
||||
search={unref(showSearchRef) && (collapsedShowSearch ? true : !collapsed)}
|
||||
beforeClickFn={beforeMenuClickFn}
|
||||
isHorizontal={props.isHorizontal}
|
||||
appendClass={unref(appendClass)}
|
||||
type={unref(getType)}
|
||||
mode={unref(getMenuMode)}
|
||||
collapsedShowTitle={unref(getCollapsedShowTitle)}
|
||||
theme={unref(getMenuTheme)}
|
||||
showLogo={unref(showLogo)}
|
||||
search={unref(showSearch)}
|
||||
items={unref(menusRef)}
|
||||
flatItems={unref(flatMenusRef)}
|
||||
accordion={unref(getAccordion)}
|
||||
onMenuClick={handleMenuClick}
|
||||
onClickSearchInput={handleClickSearchInput}
|
||||
appendClass={props.splitType === MenuSplitTyeEnum.TOP}
|
||||
isTop={props.isTop}
|
||||
accordion={accordion}
|
||||
>
|
||||
{{
|
||||
header: () =>
|
||||
isShowLogo && (
|
||||
<Logo
|
||||
showTitle={!collapsed}
|
||||
class={[`layout-menu__logo`, themeData]}
|
||||
theme={themeData}
|
||||
/>
|
||||
),
|
||||
header: () => renderHeader(),
|
||||
}}
|
||||
</BasicMenu>
|
||||
);
|
||||
|
@@ -9,17 +9,5 @@
|
||||
width: @logo-width;
|
||||
height: @logo-width;
|
||||
}
|
||||
|
||||
&.light {
|
||||
.logo-title {
|
||||
color: @text-color-base;
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.logo-title {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
113
src/layouts/default/menu/useLayoutMenu.ts
Normal file
113
src/layouts/default/menu/useLayoutMenu.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { Menu } from '/@/router/types';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { watch, unref, ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
import { useThrottle } from '/@/hooks/core/useThrottle';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
|
||||
import {
|
||||
getChildrenMenus,
|
||||
getCurrentParentPath,
|
||||
getFlatChildrenMenus,
|
||||
getFlatMenus,
|
||||
getMenus,
|
||||
getShallowMenus,
|
||||
} from '/@/router/menus';
|
||||
import { permissionStore } from '/@/store/modules/permission';
|
||||
|
||||
export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
|
||||
// Menu array
|
||||
const menusRef = ref<Menu[]>([]);
|
||||
// flat menu array
|
||||
const flatMenusRef = ref<Menu[]>([]);
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
|
||||
|
||||
const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
|
||||
|
||||
const splitNotLeft = computed(
|
||||
() => unref(splitType) !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontal)
|
||||
);
|
||||
|
||||
const splitLeft = computed(() => !unref(getSplit) || unref(splitType) !== MenuSplitTyeEnum.LEFT);
|
||||
|
||||
const spiltTop = computed(() => unref(splitType) === MenuSplitTyeEnum.TOP);
|
||||
|
||||
const normalType = computed(() => {
|
||||
return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
|
||||
});
|
||||
|
||||
watch(
|
||||
[() => unref(currentRoute).path, () => unref(splitType)],
|
||||
async ([path]: [string, MenuSplitTyeEnum]) => {
|
||||
if (unref(splitNotLeft)) return;
|
||||
|
||||
const parentPath = await getCurrentParentPath(path);
|
||||
parentPath && throttleHandleSplitLeftMenu(parentPath);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Menu changes
|
||||
watch(
|
||||
[() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
|
||||
() => {
|
||||
genMenus();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
// split Menu changes
|
||||
watch([() => getSplit.value], () => {
|
||||
if (unref(splitNotLeft)) return;
|
||||
genMenus();
|
||||
});
|
||||
|
||||
// Handle left menu split
|
||||
async function handleSplitLeftMenu(parentPath: string) {
|
||||
if (unref(splitLeft)) return;
|
||||
|
||||
// spilt mode left
|
||||
const children = await getChildrenMenus(parentPath);
|
||||
if (!children) {
|
||||
setMenuSetting({ hidden: false });
|
||||
flatMenusRef.value = [];
|
||||
menusRef.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const flatChildren = await getFlatChildrenMenus(children);
|
||||
setMenuSetting({ hidden: true });
|
||||
flatMenusRef.value = flatChildren;
|
||||
menusRef.value = children;
|
||||
}
|
||||
|
||||
// get menus
|
||||
async function genMenus() {
|
||||
// normal mode
|
||||
if (unref(normalType)) {
|
||||
flatMenusRef.value = await getFlatMenus();
|
||||
menusRef.value = await getMenus();
|
||||
return;
|
||||
}
|
||||
|
||||
// split-top
|
||||
if (unref(spiltTop)) {
|
||||
const shallowMenus = await getShallowMenus();
|
||||
|
||||
flatMenusRef.value = shallowMenus;
|
||||
menusRef.value = shallowMenus;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return { flatMenusRef, menusRef };
|
||||
}
|
@@ -33,7 +33,7 @@ export default defineComponent({
|
||||
return tabStore.getTabsState;
|
||||
});
|
||||
|
||||
// If you monitor routing changes, tab switching will be stuck. So use this method
|
||||
// If you monitor routing changes, tab switching will be stuck. So setting this method
|
||||
watch(
|
||||
() => tabStore.getLastChangeRouteState,
|
||||
() => {
|
||||
|
77
src/layouts/default/sider/LayoutSideBar.tsx
Normal file
77
src/layouts/default/sider/LayoutSideBar.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import './index.less';
|
||||
|
||||
import { computed, defineComponent, ref, unref } from 'vue';
|
||||
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutSideBar',
|
||||
setup() {
|
||||
const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const sideRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
|
||||
const { getCollapsed, getMenuWidth, getSplit, getTheme } = useMenuSetting();
|
||||
|
||||
const { getTriggerAttr, getTriggerSlot } = useTrigger();
|
||||
|
||||
const { renderDragLine } = useDragLine(sideRef, dragBarRef);
|
||||
|
||||
const {
|
||||
getCollapsedWidth,
|
||||
onBreakpointChange,
|
||||
onCollapseChange,
|
||||
onSiderClick,
|
||||
} = useSiderEvent();
|
||||
|
||||
const getMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.INLINE : null;
|
||||
});
|
||||
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
function renderDefault() {
|
||||
return (
|
||||
<>
|
||||
<LayoutMenu
|
||||
theme={unref(getTheme)}
|
||||
menuMode={unref(getMode)}
|
||||
splitType={unref(getSplitType)}
|
||||
/>
|
||||
{renderDragLine()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Layout.Sider
|
||||
ref={sideRef}
|
||||
class="layout-sidebar"
|
||||
breakpoint="md"
|
||||
collapsible
|
||||
width={unref(getMenuWidth)}
|
||||
collapsed={unref(getCollapsed)}
|
||||
collapsedWidth={unref(getCollapsedWidth)}
|
||||
theme={unref(getTheme)}
|
||||
onClick={onSiderClick}
|
||||
onCollapse={onCollapseChange}
|
||||
onBreakpoint={onBreakpointChange}
|
||||
{...unref(getTriggerAttr)}
|
||||
>
|
||||
{{
|
||||
...unref(getTriggerSlot),
|
||||
default: () => renderDefault(),
|
||||
}}
|
||||
</Layout.Sider>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
44
src/layouts/default/sider/index.less
Normal file
44
src/layouts/default/sider/index.less
Normal file
@@ -0,0 +1,44 @@
|
||||
@import (reference) '../../../design/index.less';
|
||||
|
||||
.layout-sidebar {
|
||||
background-size: 100% 100%;
|
||||
|
||||
&.ant-layout-sider-dark {
|
||||
background: @sider-dark-bg-color;
|
||||
}
|
||||
|
||||
&:not(.ant-layout-sider-dark) {
|
||||
border-right: 1px solid @border-color-light;
|
||||
}
|
||||
|
||||
.ant-layout-sider-zero-width-trigger {
|
||||
top: 40%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&__darg-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2px;
|
||||
z-index: @side-drag-z-index;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: @primary-color;
|
||||
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout-sider-trigger {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
163
src/layouts/default/sider/useLayoutSider.tsx
Normal file
163
src/layouts/default/sider/useLayoutSider.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { computed, unref, onMounted, nextTick, ref } from 'vue';
|
||||
import LayoutTrigger from '/@/layouts/default/LayoutTrigger';
|
||||
|
||||
import { TriggerEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useDebounce } from '/@/hooks/core/useDebounce';
|
||||
|
||||
/**
|
||||
* Handle related operations of menu events
|
||||
*/
|
||||
export function useSiderEvent() {
|
||||
const initRef = ref(false);
|
||||
const brokenRef = ref(false);
|
||||
const collapseRef = ref(true);
|
||||
|
||||
const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting();
|
||||
|
||||
const getCollapsedWidth = computed(() => {
|
||||
return unref(brokenRef) ? 0 : unref(getMiniWidthNumber);
|
||||
});
|
||||
|
||||
function onCollapseChange(val: boolean) {
|
||||
if (initRef.value) {
|
||||
collapseRef.value = val;
|
||||
setMenuSetting({ collapsed: val });
|
||||
} else {
|
||||
!unref(getCollapsed) && setMenuSetting({ collapsed: val });
|
||||
}
|
||||
initRef.value = true;
|
||||
}
|
||||
|
||||
function onBreakpointChange(broken: boolean) {
|
||||
brokenRef.value = broken;
|
||||
}
|
||||
|
||||
function onSiderClick(e: ChangeEvent) {
|
||||
if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
|
||||
if (!unref(getCollapsed) || !unref(getShow)) return;
|
||||
setMenuSetting({ collapsed: false });
|
||||
}
|
||||
return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle related operations of menu folding
|
||||
*/
|
||||
export function useTrigger() {
|
||||
const { getTrigger } = useMenuSetting();
|
||||
|
||||
const showTrigger = computed(() => {
|
||||
const trigger = unref(getTrigger);
|
||||
return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
|
||||
});
|
||||
|
||||
const getTriggerAttr = computed(() => {
|
||||
if (unref(showTrigger)) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
trigger: null,
|
||||
};
|
||||
});
|
||||
|
||||
const getTriggerSlot = computed(() => {
|
||||
if (unref(showTrigger)) {
|
||||
return {
|
||||
trigger: () => <LayoutTrigger />,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
return { getTriggerAttr, getTriggerSlot };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle menu drag and drop related operations
|
||||
* @param siderRef
|
||||
* @param dragBarRef
|
||||
*/
|
||||
export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
|
||||
const { getMiniWidthNumber, getCollapsed, setMenuSetting, getHasDrag } = useMenuSetting();
|
||||
|
||||
const getDragBarStyle = computed(() => {
|
||||
if (unref(getCollapsed)) {
|
||||
return { left: `${unref(getMiniWidthNumber)}px` };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const [exec] = useDebounce(changeWrapWidth, 20);
|
||||
exec();
|
||||
});
|
||||
});
|
||||
|
||||
function renderDragLine() {
|
||||
return (
|
||||
<div
|
||||
class={[`layout-sidebar__darg-bar`, !unref(getHasDrag) ? 'hide' : '']}
|
||||
style={unref(getDragBarStyle)}
|
||||
ref={dragBarRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) {
|
||||
document.onmousemove = function (innerE) {
|
||||
let iT = (ele as any).left + (innerE.clientX - clientX);
|
||||
innerE = innerE || window.event;
|
||||
const maxT = 600;
|
||||
const minT = unref(getMiniWidthNumber);
|
||||
iT < 0 && (iT = 0);
|
||||
iT > maxT && (iT = maxT);
|
||||
iT < minT && (iT = minT);
|
||||
ele.style.left = wrap.style.width = iT + 'px';
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// Drag and drop in the menu area-release the mouse
|
||||
function removeMouseup(ele: any) {
|
||||
const wrap = unref(siderRef).$el;
|
||||
document.onmouseup = function () {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
const width = parseInt(wrap.style.width);
|
||||
const miniWidth = unref(getMiniWidthNumber);
|
||||
|
||||
if (!unref(getCollapsed)) {
|
||||
width > miniWidth + 20
|
||||
? setMenuSetting({ menuWidth: width })
|
||||
: setMenuSetting({ collapsed: true });
|
||||
} else {
|
||||
width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width });
|
||||
}
|
||||
ele.releaseCapture?.();
|
||||
};
|
||||
}
|
||||
|
||||
function changeWrapWidth() {
|
||||
const ele = unref(dragBarRef) as any;
|
||||
const side = unref(siderRef);
|
||||
|
||||
const wrap = (side || {}).$el;
|
||||
ele &&
|
||||
(ele.onmousedown = (e: any) => {
|
||||
wrap.style.transition = 'unset';
|
||||
const clientX = e?.clientX;
|
||||
ele.left = ele.offsetLeft;
|
||||
handleMouseMove(ele, wrap, clientX);
|
||||
removeMouseup(ele);
|
||||
ele.setCapture?.();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return { renderDragLine };
|
||||
}
|
Reference in New Issue
Block a user