Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
ChenSun1
vue-vben-admin
提交
a65ad9ed
V
vue-vben-admin
项目概览
ChenSun1
/
vue-vben-admin
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vue-vben-admin
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
a65ad9ed
编写于
12月 15, 2020
作者:
V
vben
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
wip(menu): perf menu
上级
ec7efcf0
变更
80
隐藏空白更改
内联
并排
Showing
80 changed file
with
1335 addition
and
969 deletion
+1335
-969
CHANGELOG.zh_CN.md
CHANGELOG.zh_CN.md
+5
-1
src/components/Application/index.ts
src/components/Application/index.ts
+1
-3
src/components/Application/src/AppLocalePicker.vue
src/components/Application/src/AppLocalePicker.vue
+6
-5
src/components/Application/src/AppLogo.vue
src/components/Application/src/AppLogo.vue
+1
-1
src/components/Application/src/AppProvider.vue
src/components/Application/src/AppProvider.vue
+13
-2
src/components/Application/src/search/AppSearch.vue
src/components/Application/src/search/AppSearch.vue
+1
-1
src/components/Application/src/useAppContext.ts
src/components/Application/src/useAppContext.ts
+2
-0
src/components/Icon/index.tsx
src/components/Icon/index.tsx
+1
-1
src/components/Menu/src/BasicMenu.vue
src/components/Menu/src/BasicMenu.vue
+52
-83
src/components/Menu/src/MenuContent.tsx
src/components/Menu/src/MenuContent.tsx
+0
-96
src/components/Menu/src/components/BasicMenuItem.vue
src/components/Menu/src/components/BasicMenuItem.vue
+5
-12
src/components/Menu/src/components/BasicSubMenuItem.vue
src/components/Menu/src/components/BasicSubMenuItem.vue
+5
-5
src/components/Menu/src/components/MenuItemContent.vue
src/components/Menu/src/components/MenuItemContent.vue
+41
-0
src/components/Menu/src/components/MenuItemTag.vue
src/components/Menu/src/components/MenuItemTag.vue
+56
-0
src/components/Menu/src/index.less
src/components/Menu/src/index.less
+35
-107
src/components/Menu/src/props.ts
src/components/Menu/src/props.ts
+10
-3
src/components/Menu/src/useOpenKeys.ts
src/components/Menu/src/useOpenKeys.ts
+2
-0
src/components/Page/src/PageFooter.vue
src/components/Page/src/PageFooter.vue
+3
-1
src/components/Preview/src/index.less
src/components/Preview/src/index.less
+1
-1
src/components/Upload/src/UploadModal.vue
src/components/Upload/src/UploadModal.vue
+1
-2
src/design/color.less
src/design/color.less
+1
-1
src/design/index.less
src/design/index.less
+4
-0
src/design/var/index.less
src/design/var/index.less
+13
-1
src/hooks/event/useBreakpoint.ts
src/hooks/event/useBreakpoint.ts
+12
-7
src/hooks/setting/useHeaderSetting.ts
src/hooks/setting/useHeaderSetting.ts
+0
-5
src/hooks/setting/useMenuSetting.ts
src/hooks/setting/useMenuSetting.ts
+5
-0
src/hooks/setting/useMultipleTabSetting.ts
src/hooks/setting/useMultipleTabSetting.ts
+3
-0
src/hooks/web/useAppInject.ts
src/hooks/web/useAppInject.ts
+10
-0
src/hooks/web/useI18n.ts
src/hooks/web/useI18n.ts
+1
-1
src/hooks/web/usePage.ts
src/hooks/web/usePage.ts
+7
-5
src/hooks/web/useTabs.ts
src/hooks/web/useTabs.ts
+5
-1
src/layouts/default/content/index.vue
src/layouts/default/content/index.vue
+1
-2
src/layouts/default/header/LayoutHeader.tsx
src/layouts/default/header/LayoutHeader.tsx
+39
-115
src/layouts/default/header/LayoutMultipleHeader.less
src/layouts/default/header/LayoutMultipleHeader.less
+3
-1
src/layouts/default/header/LayoutMultipleHeader.tsx
src/layouts/default/header/LayoutMultipleHeader.tsx
+9
-7
src/layouts/default/header/UserDropdown.tsx
src/layouts/default/header/UserDropdown.tsx
+0
-125
src/layouts/default/header/components/Breadcrumb.vue
src/layouts/default/header/components/Breadcrumb.vue
+69
-11
src/layouts/default/header/components/ErrorAction.vue
src/layouts/default/header/components/ErrorAction.vue
+47
-0
src/layouts/default/header/components/FullScreen.vue
src/layouts/default/header/components/FullScreen.vue
+36
-0
src/layouts/default/header/components/index.ts
src/layouts/default/header/components/index.ts
+15
-0
src/layouts/default/header/components/lock/LockAction.less
src/layouts/default/header/components/lock/LockAction.less
+0
-0
src/layouts/default/header/components/lock/LockAction.tsx
src/layouts/default/header/components/lock/LockAction.tsx
+0
-0
src/layouts/default/header/components/lock/LockModal.vue
src/layouts/default/header/components/lock/LockModal.vue
+116
-0
src/layouts/default/header/components/lock/index.vue
src/layouts/default/header/components/lock/index.vue
+38
-0
src/layouts/default/header/components/notify/NoticeList.vue
src/layouts/default/header/components/notify/NoticeList.vue
+10
-2
src/layouts/default/header/components/notify/data.ts
src/layouts/default/header/components/notify/data.ts
+0
-0
src/layouts/default/header/components/notify/index.vue
src/layouts/default/header/components/notify/index.vue
+14
-7
src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
.../default/header/components/user-dropdown/DropMenuItem.vue
+27
-0
src/layouts/default/header/components/user-dropdown/index.vue
...layouts/default/header/components/user-dropdown/index.vue
+156
-0
src/layouts/default/header/index.less
src/layouts/default/header/index.less
+59
-172
src/layouts/default/header/index.vue
src/layouts/default/header/index.vue
+161
-0
src/layouts/default/index.vue
src/layouts/default/index.vue
+7
-14
src/layouts/default/menu/index.tsx
src/layouts/default/menu/index.tsx
+35
-11
src/layouts/default/menu/useLayoutMenu.ts
src/layouts/default/menu/useLayoutMenu.ts
+6
-6
src/layouts/default/setting/SettingDrawer.tsx
src/layouts/default/setting/SettingDrawer.tsx
+8
-1
src/layouts/default/setting/enum.ts
src/layouts/default/setting/enum.ts
+1
-0
src/layouts/default/setting/handler.ts
src/layouts/default/setting/handler.ts
+2
-0
src/layouts/default/sider/index.less
src/layouts/default/sider/index.less
+9
-3
src/layouts/default/sider/index.tsx
src/layouts/default/sider/index.tsx
+17
-48
src/layouts/default/sider/useLayoutSider.tsx
src/layouts/default/sider/useLayoutSider.tsx
+2
-7
src/layouts/default/tabs/components/TabContent.vue
src/layouts/default/tabs/components/TabContent.vue
+1
-1
src/layouts/default/tabs/components/TabRedo.vue
src/layouts/default/tabs/components/TabRedo.vue
+37
-0
src/layouts/default/tabs/index.less
src/layouts/default/tabs/index.less
+28
-21
src/layouts/default/tabs/index.vue
src/layouts/default/tabs/index.vue
+24
-29
src/layouts/default/tabs/useTabDropdown.ts
src/layouts/default/tabs/useTabDropdown.ts
+1
-5
src/layouts/default/useLayoutContext.ts
src/layouts/default/useLayoutContext.ts
+0
-1
src/locales/lang/en/layout/header.ts
src/locales/lang/en/layout/header.ts
+1
-1
src/locales/lang/en/layout/multipleTab.ts
src/locales/lang/en/layout/multipleTab.ts
+1
-0
src/locales/lang/en/layout/setting.ts
src/locales/lang/en/layout/setting.ts
+1
-0
src/locales/lang/zh_CN/layout/header.ts
src/locales/lang/zh_CN/layout/header.ts
+1
-1
src/locales/lang/zh_CN/layout/multipleTab.ts
src/locales/lang/zh_CN/layout/multipleTab.ts
+1
-0
src/locales/lang/zh_CN/layout/setting.ts
src/locales/lang/zh_CN/layout/setting.ts
+1
-0
src/logics/mitt/tabChange.ts
src/logics/mitt/tabChange.ts
+28
-0
src/router/guard/index.ts
src/router/guard/index.ts
+2
-4
src/router/menus/index.ts
src/router/menus/index.ts
+1
-0
src/settings/projectSetting.ts
src/settings/projectSetting.ts
+4
-2
src/store/modules/tab.ts
src/store/modules/tab.ts
+4
-19
src/types/config.d.ts
src/types/config.d.ts
+4
-2
src/utils/mitt.ts
src/utils/mitt.ts
+5
-5
src/views/sys/lock/LockPage.vue
src/views/sys/lock/LockPage.vue
+1
-1
未找到文件。
CHANGELOG.zh_CN.md
浏览文件 @
a65ad9ed
...
...
@@ -7,11 +7,13 @@
### ⚡ Performance Improvements
-
异步引入组件
-
优化整体结构
### 🎫 Chores
-
返回顶部样式调整,避免遮住其他元素
-
升级
`ant-design-vue`
到
`2.0.0-rc.4`
-
升级
`ant-design-vue`
到
`2.0.0-rc.5`
-
刷新按钮布局调整
### 🐛 Bug Fixes
...
...
@@ -23,6 +25,8 @@
-
修复按钮样式问题
-
修复菜单分割模式问题
-
修复
`Modal`
与
`Drawer`
组件在使用 emits 数据传递失效问题
-
修复菜单已知问题
-
修复上传组件 api 失效问题
## 2.0.0-rc.13 (2020-12-10)
...
...
src/components/Application/index.ts
浏览文件 @
a65ad9ed
...
...
@@ -2,9 +2,7 @@ import { withInstall } from '../util';
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
import
AppLogo
from
'
./src/AppLogo.vue
'
;
export
const
AppLocalePicker
=
createAsyncComponent
(()
=>
import
(
'
./src/AppLocalePicker.vue
'
),
{
loading
:
true
,
});
export
const
AppLocalePicker
=
createAsyncComponent
(()
=>
import
(
'
./src/AppLocalePicker.vue
'
));
export
const
AppProvider
=
createAsyncComponent
(()
=>
import
(
'
./src/AppProvider.vue
'
));
export
const
AppSearch
=
createAsyncComponent
(()
=>
import
(
'
./src/search/AppSearch.vue
'
),
{
loading
:
true
,
...
...
src/components/Application/src/AppLocalePicker.vue
浏览文件 @
a65ad9ed
...
...
@@ -11,8 +11,8 @@
:overlayClassName=
"`$
{prefixCls}-overlay`"
>
<span
:class=
"prefixCls"
>
<
GlobalOutlined
:class=
"`$
{prefixCls}__icon`
" />
<span
v-if=
"showText"
>
{{
getLangText
}}
</span>
<
Icon
icon=
"cil:language
"
/>
<span
v-if=
"showText"
:class=
"`$
{prefixCls}__text`"
>
{{
getLangText
}}
</span>
</span>
</Dropdown>
</
template
>
...
...
@@ -30,9 +30,10 @@
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
Icon
from
'
/@/components/Icon
'
;
export
default
defineComponent
({
name
:
'
AppLocalPicker
'
,
components
:
{
GlobalOutlined
,
Dropdown
},
components
:
{
GlobalOutlined
,
Dropdown
,
Icon
},
props
:
{
// Whether to display text
showText
:
propTypes
.
bool
.
def
(
true
),
...
...
@@ -88,8 +89,8 @@
align-items: center;
cursor: pointer;
&__
icon
{
margin-
right: 4
px;
&__
text
{
margin-
left: 6
px;
}
}
</
style
>
src/components/Application/src/AppLogo.vue
浏览文件 @
a65ad9ed
...
...
@@ -87,7 +87,7 @@
}
&__title {
font-size: 1
8
px;
font-size: 1
6
px;
font-weight: 700;
opacity: 0;
transition: all 0.5s;
...
...
src/components/Application/src/AppProvider.vue
浏览文件 @
a65ad9ed
...
...
@@ -3,11 +3,13 @@
</
template
>
<
script
lang=
"ts"
>
import
type
{
PropType
}
from
'
vue
'
;
import
{
defineComponent
,
toRefs
}
from
'
vue
'
;
import
{
defineComponent
,
toRefs
,
ref
}
from
'
vue
'
;
import
{
createAppProviderContext
}
from
'
./useAppContext
'
;
import
designSetting
from
'
/@/settings/designSetting
'
;
import
{
createBreakpointListen
}
from
'
/@/hooks/event/useBreakpoint
'
;
export
default
defineComponent
({
name
:
'
AppProvider
'
,
inheritAttrs
:
false
,
...
...
@@ -18,8 +20,17 @@
},
},
setup
(
props
)
{
const
isMobileRef
=
ref
(
false
);
createBreakpointListen
(({
screenMap
,
sizeEnum
,
width
})
=>
{
const
lgWidth
=
screenMap
.
get
(
sizeEnum
.
LG
);
if
(
lgWidth
)
{
isMobileRef
.
value
=
width
.
value
-
1
<
lgWidth
;
}
});
const
{
prefixCls
}
=
toRefs
(
props
);
createAppProviderContext
({
prefixCls
});
createAppProviderContext
({
prefixCls
,
isMobile
:
isMobileRef
});
return
{};
},
});
...
...
src/components/Application/src/search/AppSearch.vue
浏览文件 @
a65ad9ed
...
...
@@ -50,6 +50,6 @@
@prefix-cls: ~'@{namespace}-app-search';
.@{prefix-cls} {
padding:
0 10
px;
padding:
2
px;
}
</
style
>
src/components/Application/src/useAppContext.ts
浏览文件 @
a65ad9ed
...
...
@@ -3,6 +3,8 @@ import { createContext, useContext } from '/@/hooks/core/useContext';
export
interface
AppProviderContextProps
{
prefixCls
:
Ref
<
string
>
;
isMobile
:
Ref
<
boolean
>
;
}
const
key
:
InjectionKey
<
AppProviderContextProps
>
=
Symbol
();
...
...
src/components/Icon/index.tsx
浏览文件 @
a65ad9ed
...
...
@@ -77,7 +77,7 @@ export default defineComponent({
onMounted
(
update
);
return
()
=>
(
<
div
ref
=
{
elRef
}
class
=
{
[
attrs
.
class
,
'
app-iconify anticon
'
]
}
style
=
{
unref
(
wrapStyleRef
)
}
/>
<
span
ref
=
{
elRef
}
class
=
{
[
attrs
.
class
,
'
app-iconify anticon
'
]
}
style
=
{
unref
(
wrapStyleRef
)
}
/>
);
},
});
src/components/Menu/src/BasicMenu.vue
浏览文件 @
a65ad9ed
<
template
>
<slot
name=
"header"
v-if=
"!getIsHorizontal"
/>
<ScrollContainer
:class=
"`$
{prefixCls}-wrapper`" :style="getWrapperStyle">
<Menu
:selectedKeys=
"selectedKeys"
:defaultSelectedKeys=
"defaultSelectedKeys"
:mode=
"mode"
:openKeys=
"getOpenKeys"
:inlineIndent=
"inlineIndent"
:theme=
"theme"
@
openChange=
"handleOpenChange"
:class=
"getMenuClass"
@
click=
"handleMenuClick"
:subMenuOpenDelay=
"0.2"
v-bind=
"getInlineCollapseOptions"
>
<template
v-for=
"item in items"
:key=
"item.path"
>
<BasicSubMenuItem
:item=
"item"
:theme=
"theme"
:level=
"1"
:appendClass=
"appendClass"
:parentPath=
"currentParentPath"
:showTitle=
"showTitle"
:isHorizontal=
"isHorizontal"
/>
</
template
>
</Menu>
</ScrollContainer>
<Menu
:selectedKeys=
"selectedKeys"
:defaultSelectedKeys=
"defaultSelectedKeys"
:mode=
"mode"
:openKeys=
"getOpenKeys"
:inlineIndent=
"inlineIndent"
:theme=
"theme"
@
openChange=
"handleOpenChange"
:class=
"getMenuClass"
@
click=
"handleMenuClick"
:subMenuOpenDelay=
"0.2"
v-bind=
"getInlineCollapseOptions"
>
<template
v-for=
"item in items"
:key=
"item.path"
>
<BasicSubMenuItem
:item=
"item"
:theme=
"theme"
:level=
"1"
:showTitle=
"showTitle"
:isHorizontal=
"isHorizontal"
/>
</
template
>
</Menu>
</template>
<
script
lang=
"ts"
>
import
type
{
MenuState
}
from
'
./types
'
;
import
{
computed
,
defineComponent
,
unref
,
reactive
,
watch
,
toRefs
,
ref
,
CSSProperties
,
}
from
'
vue
'
;
import
{
computed
,
defineComponent
,
unref
,
reactive
,
watch
,
toRefs
,
ref
}
from
'
vue
'
;
import
{
Menu
}
from
'
ant-design-vue
'
;
import
BasicSubMenuItem
from
'
./components/BasicSubMenuItem.vue
'
;
import
{
ScrollContainer
}
from
'
/@/components/Container
'
;
import
{
MenuModeEnum
,
MenuTypeEnum
}
from
'
/@/enums/menuEnum
'
;
import
{
appStore
}
from
'
/@/store/modules/app
'
;
import
{
useOpenKeys
}
from
'
./useOpenKeys
'
;
import
{
useRouter
}
from
'
vue-router
'
;
import
{
RouteLocationNormalizedLoaded
,
useRouter
}
from
'
vue-router
'
;
import
{
isFunction
}
from
'
/@/utils/is
'
;
import
{
getCurrentParentPath
}
from
'
/@/router/menus
'
;
import
{
basicProps
}
from
'
./props
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
REDIRECT_NAME
}
from
'
/@/router/constant
'
;
import
{
tabStore
}
from
'
/@/store/modules/tab
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
getCurrentParentPath
}
from
'
/@/router/menus
'
;
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import
{
listenerLastChangeTab
}
from
'
/@/logics/mitt/tabChange
'
;
export
default
defineComponent
({
name
:
'
BasicMenu
'
,
components
:
{
Menu
,
ScrollContainer
,
BasicSubMenuItem
,
// BasicSubMenuItem: createAsyncComponent(() => import('./components/BasicSubMenuItem.vue')),
},
props
:
basicProps
,
emits
:
[
'
menuClick
'
],
setup
(
props
,
{
emit
})
{
const
currentParentPath
=
ref
(
''
);
const
isClickGo
=
ref
(
false
);
const
menuState
=
reactive
<
MenuState
>
({
...
...
@@ -97,18 +80,24 @@
accordion
);
const
get
MenuClass
=
computed
(()
=>
{
const
get
IsTopMenu
=
computed
(()
=>
{
const
{
type
,
mode
}
=
props
;
return
(
(
type
===
MenuTypeEnum
.
TOP_MENU
&&
mode
===
MenuModeEnum
.
HORIZONTAL
)
||
(
props
.
isHorizontal
&&
unref
(
getSplit
))
);
});
const
getMenuClass
=
computed
(()
=>
{
return
[
prefixCls
,
`justify-
${
unref
(
getTopMenuAlign
)}
`
,
{
[
`
${
prefixCls
}
--hide-title`
]:
!
unref
(
showTitle
),
[
`
${
prefixCls
}
--collapsed-show-title`
]:
props
.
collapsedShowTitle
,
[
`
${
prefixCls
}
__second`
]:
!
props
.
isHorizontal
&&
appStore
.
getProjectConfig
.
menuSetting
.
split
,
[
`
${
prefixCls
}
__sidebar-hor`
]:
type
===
MenuTypeEnum
.
TOP_MENU
&&
mode
===
MenuModeEnum
.
HORIZONTAL
,
[
`
${
prefixCls
}
__second`
]:
!
props
.
isHorizontal
&&
unref
(
getSplit
),
[
`
${
prefixCls
}
__sidebar-hor`
]:
unref
(
getIsTopMenu
),
},
];
});
...
...
@@ -125,23 +114,10 @@
return
inlineCollapseOptions
;
});
const
getWrapperStyle
=
computed
(
():
CSSProperties
=>
{
return
{
height
:
`calc(100% -
${
props
.
showLogo
?
'
48px
'
:
'
0px
'
}
)`
,
overflowY
:
'
hidden
'
,
};
}
);
watch
(
()
=>
tabStore
.
getCurrentTab
,
()
=>
{
if
(
unref
(
currentRoute
).
name
===
REDIRECT_NAME
)
return
;
handleMenuChange
();
unref
(
getSplit
)
&&
getParentPath
();
}
);
listenerLastChangeTab
((
route
)
=>
{
if
(
route
.
name
===
REDIRECT_NAME
)
return
;
handleMenuChange
(
route
);
},
false
);
watch
(
()
=>
props
.
items
,
...
...
@@ -153,16 +129,6 @@
}
);
getParentPath
();
async
function
getParentPath
()
{
const
{
appendClass
}
=
props
;
if
(
!
appendClass
)
return
''
;
const
parentPath
=
await
getCurrentParentPath
(
unref
(
currentRoute
).
path
);
currentParentPath
.
value
=
parentPath
;
}
async
function
handleMenuClick
({
key
,
keyPath
}:
{
key
:
string
;
keyPath
:
string
[]
})
{
const
{
beforeClickFn
}
=
props
;
if
(
beforeClickFn
&&
isFunction
(
beforeClickFn
))
{
...
...
@@ -176,28 +142,31 @@
menuState
.
selectedKeys
=
[
key
];
}
function
handleMenuChange
(
)
{
async
function
handleMenuChange
(
route
?:
RouteLocationNormalizedLoaded
)
{
if
(
unref
(
isClickGo
))
{
isClickGo
.
value
=
false
;
return
;
}
const
path
=
unref
(
currentRoute
).
path
;
const
path
=
(
route
||
unref
(
currentRoute
)
).
path
;
if
(
props
.
mode
!==
MenuModeEnum
.
HORIZONTAL
)
{
setOpenKeys
(
path
);
}
menuState
.
selectedKeys
=
[
path
];
if
(
unref
(
getIsTopMenu
))
{
const
parentPath
=
await
getCurrentParentPath
(
path
);
menuState
.
selectedKeys
=
[
parentPath
];
}
else
{
menuState
.
selectedKeys
=
[
path
];
}
}
return
{
prefixCls
,
getIsHorizontal
,
getWrapperStyle
,
handleMenuClick
,
getInlineCollapseOptions
,
getMenuClass
,
handleOpenChange
,
getOpenKeys
,
currentParentPath
,
showTitle
,
...
toRefs
(
menuState
),
};
...
...
src/components/Menu/src/MenuContent.tsx
已删除
100644 → 0
浏览文件 @
ec7efcf0
import
type
{
Menu
as
MenuType
}
from
'
/@/router/types
'
;
import
type
{
PropType
}
from
'
vue
'
;
import
{
computed
,
unref
}
from
'
vue
'
;
import
{
defineComponent
}
from
'
vue
'
;
import
Icon
from
'
/@/components/Icon/index
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
const
{
t
}
=
useI18n
();
export
default
defineComponent
({
name
:
'
MenuContent
'
,
props
:
{
item
:
{
type
:
Object
as
PropType
<
MenuType
>
,
default
:
null
,
},
showTitle
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
default
:
true
,
},
level
:
{
type
:
Number
as
PropType
<
number
>
,
default
:
0
,
},
isHorizontal
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
default
:
true
,
},
},
setup
(
props
)
{
const
{
prefixCls
}
=
useDesign
(
'
basic-menu
'
);
const
getI18nName
=
computed
(()
=>
t
(
props
.
item
?.
name
));
const
getTagClass
=
computed
(()
=>
{
const
{
item
}
=
props
;
const
{
tag
=
{}
}
=
item
||
{};
const
{
dot
,
type
=
'
error
'
}
=
tag
;
return
[
`
${
prefixCls
}
__tag`
,
type
,
{
dot
,
},
];
});
const
getNameClass
=
computed
(()
=>
{
const
{
showTitle
}
=
props
;
return
{
[
`
${
prefixCls
}
--show-title`
]:
showTitle
,
[
`
${
prefixCls
}
__name`
]:
!
showTitle
};
});
/**
* @description: 渲染图标
*/
function
renderIcon
(
icon
?:
string
)
{
return
icon
?
<
Icon
icon
=
{
icon
}
size
=
{
18
}
class
=
"menu-item-icon"
/>
:
null
;
}
function
renderTag
()
{
const
{
item
,
showTitle
,
isHorizontal
}
=
props
;
if
(
!
item
||
showTitle
||
isHorizontal
)
return
null
;
const
{
tag
}
=
item
;
if
(
!
tag
)
return
null
;
const
{
dot
,
content
}
=
tag
;
if
(
!
dot
&&
!
content
)
return
null
;
return
<
span
class
=
{
unref
(
getTagClass
)
}
>
{
dot
?
''
:
content
}
</
span
>;
}
return
()
=>
{
const
{
item
}
=
props
;
if
(
!
item
)
{
return
null
;
}
const
{
icon
}
=
item
;
const
name
=
unref
(
getI18nName
);
return
(
<
span
class
=
{
`
${
prefixCls
}
__content-wrapper`
}
>
{
renderIcon
(
icon
)
}
{
<
span
class
=
{
unref
(
getNameClass
)
}
>
{
name
}
{
renderTag
()
}
</
span
>
}
</
span
>
);
};
},
});
src/components/Menu/src/components/BasicMenuItem.vue
浏览文件 @
a65ad9ed
<
template
>
<MenuItem
:class=
"getLevelClass"
>
<MenuContent
v-bind=
"$props"
:item=
"item"
/>
<Menu
Item
Content
v-bind=
"$props"
:item=
"item"
/>
</MenuItem>
</
template
>
<
script
lang=
"ts"
>
...
...
@@ -9,25 +9,18 @@
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
itemProps
}
from
'
../props
'
;
import
Menu
Content
from
'
../MenuContent
'
;
import
Menu
ItemContent
from
'
./MenuItemContent.vue
'
;
export
default
defineComponent
({
name
:
'
BasicMenuItem
'
,
components
:
{
MenuItem
:
Menu
.
Item
,
MenuContent
},
components
:
{
MenuItem
:
Menu
.
Item
,
Menu
Item
Content
},
props
:
itemProps
,
setup
(
props
)
{
const
{
prefixCls
}
=
useDesign
(
'
basic-menu-item
'
);
const
getLevelClass
=
computed
(()
=>
{
const
{
appendClass
,
level
,
item
,
parentPath
,
theme
}
=
props
;
const
isAppendActiveCls
=
appendClass
&&
level
===
1
&&
item
.
path
===
parentPath
;
const
{
level
,
theme
}
=
props
;
const
levelCls
=
[
`
${
prefixCls
}
__level
${
level
}
`
,
theme
,
{
'
top-active-menu
'
:
isAppendActiveCls
,
},
];
const
levelCls
=
[
`
${
prefixCls
}
__level
${
level
}
`
,
theme
];
return
levelCls
;
});
return
{
...
...
src/components/Menu/src/components/BasicSubMenuItem.vue
浏览文件 @
a65ad9ed
...
...
@@ -2,7 +2,7 @@
<BasicMenuItem
v-if=
"!menuHasChildren(item)"
v-bind=
"$props"
/>
<SubMenu
v-else
:class=
"[`$
{prefixCls}__level${level}`, theme]">
<template
#title
>
<MenuContent
v-bind=
"$props"
:item=
"item"
/>
<Menu
Item
Content
v-bind=
"$props"
:item=
"item"
/>
</
template
>
<!-- <template #expandIcon="{ key }">
<ExpandIcon :key="key" />
...
...
@@ -21,17 +21,17 @@
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
itemProps
}
from
'
../props
'
;
import
BasicMenuItem
from
'
./BasicMenuItem.vue
'
;
import
MenuContent
from
'
../MenuContent
'
;
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import
MenuItemContent
from
'
./MenuItemContent.vue
'
;
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
export
default
defineComponent
({
name
:
'
BasicSubMenuItem
'
,
isSubMenu
:
true
,
components
:
{
BasicMenuItem
,
SubMenu
:
Menu
.
SubMenu
,
MenuItem
:
Menu
.
Item
,
MenuContent
,
Menu
Item
Content
,
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
},
props
:
itemProps
,
...
...
src/components/Menu/src/components/MenuItemContent.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<span
:class=
"`$
{prefixCls}-wrapper`">
<Icon
v-if=
"getIcon"
:icon=
"getIcon"
:size=
"18"
:class=
"`$
{prefixCls}-wrapper__icon`" />
<span
:class=
"getNameClass"
>
{{
getI18nName
}}
<MenuItemTag
v-bind=
"$props"
/>
</span>
</span>
</
template
>
<
script
lang=
"ts"
>
import
{
computed
,
defineComponent
}
from
'
vue
'
;
import
Icon
from
'
/@/components/Icon/index
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
contentProps
}
from
'
../props
'
;
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
const
{
t
}
=
useI18n
();
export
default
defineComponent
({
name
:
'
MenuItemContent
'
,
components
:
{
Icon
,
MenuItemTag
:
createAsyncComponent
(()
=>
import
(
'
./MenuItemTag.vue
'
))
},
props
:
contentProps
,
setup
(
props
)
{
const
{
prefixCls
}
=
useDesign
(
'
basic-menu-item-content
'
);
const
getI18nName
=
computed
(()
=>
t
(
props
.
item
?.
name
));
const
getIcon
=
computed
(()
=>
props
.
item
?.
icon
);
const
getNameClass
=
computed
(()
=>
{
const
{
showTitle
}
=
props
;
return
{
[
`
${
prefixCls
}
--show-title`
]:
showTitle
,
[
`
${
prefixCls
}
__name`
]:
!
showTitle
};
});
return
{
prefixCls
,
getNameClass
,
getI18nName
,
getIcon
,
};
},
});
</
script
>
src/components/Menu/src/components/MenuItemTag.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<span
:class=
"getTagClass"
v-if=
"getShowTag"
>
{{
getContent
}}
</span>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
computed
}
from
'
vue
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
contentProps
}
from
'
../props
'
;
export
default
defineComponent
({
name
:
'
MenuItemTag
'
,
props
:
contentProps
,
setup
(
props
)
{
const
{
prefixCls
}
=
useDesign
(
'
basic-menu-item-tag
'
);
const
getShowTag
=
computed
(()
=>
{
const
{
item
,
showTitle
,
isHorizontal
}
=
props
;
if
(
!
item
||
showTitle
||
isHorizontal
)
return
false
;
const
{
tag
}
=
item
;
if
(
!
tag
)
return
false
;
const
{
dot
,
content
}
=
tag
;
if
(
!
dot
&&
!
content
)
return
false
;
return
true
;
});
const
getContent
=
computed
(()
=>
{
if
(
!
getShowTag
.
value
)
return
''
;
const
{
item
}
=
props
;
const
{
tag
}
=
item
;
const
{
dot
,
content
}
=
tag
!
;
return
dot
?
''
:
content
;
});
const
getTagClass
=
computed
(()
=>
{
const
{
item
}
=
props
;
const
{
tag
=
{}
}
=
item
||
{};
const
{
dot
,
type
=
'
error
'
}
=
tag
;
return
[
prefixCls
,
[
`
${
prefixCls
}
--
${
type
}
`
],
{
[
`
${
prefixCls
}
--dot`
]:
dot
,
},
];
});
return
{
prefixCls
,
getTagClass
,
getShowTag
,
getContent
,
};
},
});
</
script
>
src/components/Menu/src/index.less
浏览文件 @
a65ad9ed
@import (reference) '../../../design/index.less';
@basic-menu-prefix-cls: ~'@{namespace}-basic-menu';
@basic-menu-content-prefix-cls: ~'@{namespace}-basic-menu-item-content';
@basic-menu-tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
.active-style() {
color: @white;
...
...
@@ -41,7 +43,7 @@
// }
// collapsed show title start
&
--show-title {
.@{basic-menu-content-prefix-cls}
--show-title {
max-width: unset !important;
opacity: 1 !important;
}
...
...
@@ -78,104 +80,34 @@
& > li > .ant-menu-submenu-title {
line-height: 24px;
}
.@{basic-menu-
prefix-cls}__content
-wrapper {
.@{basic-menu-
content-prefix-cls}
-wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.@{basic-menu-prefix-cls}--show-title {
.@{basic-menu-
content-
prefix-cls}--show-title {
line-height: 30px;
}
}
}
.ant-menu-item {
transition: unset;
}
// scrollbar -s tart
// &-wrapper {
/* 滚动槽 */
// &::-webkit-scrollbar {
// width: 5px;
// height: 5px;
// }
// &::-webkit-scrollbar-track {
// background: rgba(0, 0, 0, 0);
// }
// &::-webkit-scrollbar-thumb {
// background: rgba(255, 255, 255, 0.2);
// border-radius: 3px;
// box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
// }
// ::-webkit-scrollbar-thumb:hover {
// background: @border-color-dark;
// }
// }
// scrollbar end
.@{basic-menu-content-prefix-cls}-wrapper {
width: 100%;
&-item__level1.light {
&.top-active-menu {
top: 0 !important;
&__icon {
vertical-align: text-top;
}
}
&.top-active-menu:not(.ant-menu-item-selected) {
color: @primary-color;
border-bottom: 3px solid @primary-color;
}
.ant-menu-item {
transition: unset;
}
&__sidebar-hor {
// overflow: hidden;
&.ant-menu-horizontal {
display: flex;
border: 0;
align-items: center;
.@{basic-menu-prefix-cls}-item__level1 {
margin-right: 2px;
}
&.ant-menu-light {
.ant-menu-item {
&.@{basic-menu-prefix-cls}-item__level1 {
height: @header-height;
line-height: @header-height;
}
}
.ant-menu-submenu:hover,
.ant-menu-item-open,
.ant-menu-submenu-open,
.ant-menu-item-selected,
.ant-menu-submenu-selected,
.ant-menu-item:hover,
.ant-menu-item-active,
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open,
.ant-menu-submenu-active,
.ant-menu-submenu-title:hover {
color: @primary-color !important;
border-bottom: 3px solid @primary-color;
}
.ant-menu-submenu {
&:hover {
border-bottom: 3px solid @primary-color;
}
&.ant-menu-selected,
&.ant-menu-submenu-selected {
border-bottom: 3px solid @primary-color;
}
}
}
&.ant-menu-dark {
background: transparent;
...
...
@@ -204,12 +136,6 @@
.@{basic-menu-prefix-cls}-item__level1 {
background: transparent;
&.top-active-menu {
color: @white;
background: @top-menu-active-bg-color;
border-radius: 2px 2px 0 0;
}
&.ant-menu-item-selected,
&.ant-menu-submenu-selected {
background: @top-menu-active-bg-color !important;
...
...
@@ -292,7 +218,7 @@
}
&.ant-menu-light:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
overflow: hidden;
//
overflow: hidden;
border-right: none;
.ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
...
...
@@ -301,26 +227,32 @@
}
}
// 激活的子菜单样式
.ant-menu-item.ant-menu-item-selected {
position: relative;
}
&.@{basic-menu-prefix-cls}__second.ant-menu-inline-collapsed:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
// Reset menu item row height
.ant-menu-item,
.ant-menu-sub.ant-menu-inline > .ant-menu-item,
.ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title {
.@{basic-menu-prefix-cls}-item__level1 {
display: flex;
height: @app-menu-item-height * 1.4;
padding: 6px 0 !important;
margin: 0;
font-size: 12px;
line-height: @app-menu-item-height;
align-items: center;
text-align: center;
> div {
padding: 6px 0 !important;
font-size: 12px;
}
.@{basic-menu-content-prefix-cls}__name {
display: inline-block;
width: 50%;
overflow: hidden;
}
}
}
.@{basic-menu-
prefix-cls}__tag
{
.@{basic-menu-
tag-prefix-cls}
{
position: absolute;
top: calc(50% - 8px);
right: 30px;
...
...
@@ -332,7 +264,7 @@
color: #fff;
border-radius: 2px;
&
.
dot {
&
--
dot {
top: calc(50% - 4px);
width: 8px;
height: 8px;
...
...
@@ -340,19 +272,19 @@
border-radius: 50%;
}
&
.
primary {
&
--
primary {
background: @primary-color;
}
&
.
error {
&
--
error {
background: @error-color;
}
&
.
success {
&
--
success {
background: @success-color;
}
&
.
warn {
&
--
warn {
background: @warning-color;
}
}
...
...
@@ -362,10 +294,6 @@
transition: unset;
}
// .ant-menu-submenu-arrow {
// transition: all 0.15s ease 0s;
// }
.ant-menu-inline.ant-menu-sub {
box-shadow: unset !important;
transition: unset;
...
...
@@ -384,10 +312,10 @@
// collapsed show title end
.ant-menu-item,
.ant-menu-submenu-title {
> .@{basic-menu-prefix-cls}__name {
> .@{basic-menu-
content-
prefix-cls}__name {
width: 100%;
.@{basic-menu-
prefix-cls}__tag
{
.@{basic-menu-
tag-prefix-cls}
{
float: right;
margin-top: @app-menu-item-height / 2;
transform: translate(0%, -50%);
...
...
src/components/Menu/src/props.ts
浏览文件 @
a65ad9ed
...
...
@@ -9,7 +9,6 @@ export const basicProps = {
type
:
Array
as
PropType
<
Menu
[]
>
,
default
:
()
=>
[],
},
appendClass
:
propTypes
.
bool
,
collapsedShowTitle
:
propTypes
.
bool
,
...
...
@@ -42,8 +41,16 @@ export const itemProps = {
},
level
:
propTypes
.
number
,
theme
:
propTypes
.
oneOf
([
'
dark
'
,
'
light
'
]),
appendClass
:
propTypes
.
bool
,
parentPath
:
propTypes
.
string
,
showTitle
:
propTypes
.
bool
,
isHorizontal
:
propTypes
.
bool
,
};
export
const
contentProps
=
{
item
:
{
type
:
Object
as
PropType
<
Menu
>
,
default
:
null
,
},
showTitle
:
propTypes
.
bool
.
def
(
true
),
level
:
propTypes
.
number
.
def
(
0
),
isHorizontal
:
propTypes
.
bool
.
def
(
true
),
};
src/components/Menu/src/useOpenKeys.ts
浏览文件 @
a65ad9ed
...
...
@@ -42,6 +42,8 @@ export function useOpenKeys(
if
(
unref
(
mode
)
===
MenuModeEnum
.
HORIZONTAL
||
!
unref
(
accordion
))
{
menuState
.
openKeys
=
openKeys
;
}
else
{
// const menuList = toRaw(menus.value);
// getAllParentPath(menuList, path);
const
rootSubMenuKeys
:
string
[]
=
[];
for
(
const
{
children
,
path
}
of
unref
(
menus
))
{
if
(
children
&&
children
.
length
>
0
)
{
...
...
src/components/Page/src/PageFooter.vue
浏览文件 @
a65ad9ed
...
...
@@ -22,11 +22,13 @@
});
</
script
>
<
style
lang=
"less"
scoped
>
@import (reference) '../../../design/index.less';
.app-footer {
position: fixed;
right: 0;
bottom: 0;
z-index:
99
;
z-index:
@page-footer-z-index
;
display: flex;
width: 100%;
align-items: center;
...
...
src/components/Preview/src/index.less
浏览文件 @
a65ad9ed
...
...
@@ -6,7 +6,7 @@
right: 0;
bottom: 0;
left: 0;
z-index:
1000
;
z-index:
@preview-comp-z-index
;
background: rgba(0, 0, 0, 0.5);
user-select: none;
...
...
src/components/Upload/src/UploadModal.vue
浏览文件 @
a65ad9ed
...
...
@@ -55,7 +55,6 @@
import
{
checkFileType
,
checkImgType
,
getBase64WithFile
}
from
'
./helper
'
;
import
{
buildUUID
}
from
'
/@/utils/uuid
'
;
import
{
createImgPreview
}
from
'
/@/components/Preview/index
'
;
import
{
uploadApi
}
from
'
/@/api/sys/upload
'
;
import
{
isFunction
}
from
'
/@/utils/is
'
;
import
{
warn
}
from
'
/@/utils/log
'
;
import
FileList
from
'
./FileList
'
;
...
...
@@ -176,7 +175,7 @@
}
try
{
item
.
status
=
UploadResultStatus
.
UPLOADING
;
const
{
data
}
=
await
uploadApi
(
const
{
data
}
=
await
props
.
api
?.
(
{
...(
props
.
uploadParams
||
{}),
file
:
item
.
file
,
...
...
src/design/color.less
浏览文件 @
a65ad9ed
...
...
@@ -128,7 +128,7 @@
// =================================
// ==============breadcrumb=========
// =================================
@breadcrumb-item-normal-color: #
6e90a7
;
@breadcrumb-item-normal-color: #
999
;
// =================================
// ==============button=============
// =================================
...
...
src/design/index.less
浏览文件 @
a65ad9ed
...
...
@@ -31,6 +31,10 @@ html,
background-color: #fff !important;
}
html {
overflow: hidden;
}
html,
body {
width: 100%;
...
...
src/design/var/index.less
浏览文件 @
a65ad9ed
...
...
@@ -11,12 +11,24 @@
@header-height: 48px;
// logo width
@logo-width: 3
6
px;
@logo-width: 3
2
px;
//
@side-drag-z-index: 200;
@page-loading-z-index: 10000;
@lock-page-z-index: 3000;
@layout-header-fixed-z-index: 500;
@multiple-tab-fixed-z-index: 505;
@layout-sider-fixed-z-index: 510;
@preview-comp-z-index: 1000;
@page-footer-z-index: 99;
// left-menu
@app-menu-item-height: 42px;
src/hooks/event/useBreakpoint.ts
浏览文件 @
a65ad9ed
...
...
@@ -10,6 +10,9 @@ export interface CreateCallbackParams {
screen
:
ComputedRef
<
sizeEnum
|
undefined
>
;
width
:
ComputedRef
<
number
>
;
realWidth
:
ComputedRef
<
number
>
;
screenEnum
:
typeof
screenEnum
;
screenMap
:
Map
<
sizeEnum
,
number
>
;
sizeEnum
:
typeof
sizeEnum
;
}
export
function
useBreakpoint
()
{
...
...
@@ -54,8 +57,8 @@ export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void)
name
:
'
resize
'
,
listener
:
()
=>
{
resizeFn
();
getWindowWidth
();
resizeFn
();
},
});
...
...
@@ -65,12 +68,14 @@ export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void)
globalRealWidthRef
=
computed
(():
number
=>
unref
(
realWidthRef
));
function
resizeFn
()
{
fn
&&
fn
({
screen
:
globalScreenRef
,
width
:
globalWidthRef
,
realWidth
:
globalRealWidthRef
,
});
fn
?.({
screen
:
globalScreenRef
,
width
:
globalWidthRef
,
realWidth
:
globalRealWidthRef
,
screenEnum
,
screenMap
,
sizeEnum
,
});
}
resizeFn
();
...
...
src/hooks/setting/useHeaderSetting.ts
浏览文件 @
a65ad9ed
...
...
@@ -4,7 +4,6 @@ import { computed, unref } from 'vue';
import
{
appStore
}
from
'
/@/store/modules/app
'
;
import
{
useMultipleTabSetting
}
from
'
/@/hooks/setting/useMultipleTabSetting
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
useRootSetting
}
from
'
/@/hooks/setting/useRootSetting
'
;
import
{
useFullContent
}
from
'
/@/hooks/web/useFullContent
'
;
...
...
@@ -12,7 +11,6 @@ import { useFullContent } from '/@/hooks/web/useFullContent';
import
{
MenuModeEnum
}
from
'
/@/enums/menuEnum
'
;
const
{
getFullContent
}
=
useFullContent
();
const
{
getShowMultipleTab
}
=
useMultipleTabSetting
();
const
{
getMenuMode
,
getSplit
,
...
...
@@ -53,8 +51,6 @@ const getHeaderBgColor = computed(() => unref(getHeaderSetting).bgColor);
const
getShowSearch
=
computed
(()
=>
unref
(
getHeaderSetting
).
showSearch
);
const
getShowRedo
=
computed
(()
=>
unref
(
getHeaderSetting
).
showRedo
&&
unref
(
getShowMultipleTab
));
const
getUseLockPage
=
computed
(()
=>
unref
(
getHeaderSetting
).
useLockPage
);
const
getShowFullScreen
=
computed
(()
=>
unref
(
getHeaderSetting
).
showFullScreen
);
...
...
@@ -91,7 +87,6 @@ export function useHeaderSetting() {
getShowDoc
,
getShowSearch
,
getHeaderTheme
,
getShowRedo
,
getUseLockPage
,
getShowFullScreen
,
getShowNotice
,
...
...
src/hooks/setting/useMenuSetting.ts
浏览文件 @
a65ad9ed
...
...
@@ -65,6 +65,10 @@ const getIsHorizontal = computed(() => {
return
unref
(
getMenuMode
)
===
MenuModeEnum
.
HORIZONTAL
;
});
const
getIsMixMode
=
computed
(()
=>
{
return
unref
(
getMenuMode
)
===
MenuModeEnum
.
INLINE
&&
unref
(
getMenuType
)
===
MenuTypeEnum
.
MIX
;
});
const
getRealWidth
=
computed
(()
=>
{
return
unref
(
getCollapsed
)
?
unref
(
getMiniWidthNumber
)
:
unref
(
getMenuWidth
);
});
...
...
@@ -130,5 +134,6 @@ export function useMenuSetting() {
getIsTopMenu
,
getMenuBgColor
,
getShowSidebar
,
getIsMixMode
,
};
}
src/hooks/setting/useMultipleTabSetting.ts
浏览文件 @
a65ad9ed
...
...
@@ -10,6 +10,8 @@ const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show);
const
getShowQuick
=
computed
(()
=>
unref
(
getMultipleTabSetting
).
showQuick
);
const
getShowRedo
=
computed
(()
=>
unref
(
getMultipleTabSetting
).
showRedo
);
function
setMultipleTabSetting
(
multiTabsSetting
:
Partial
<
MultiTabsSetting
>
)
{
appStore
.
commitProjectConfigState
({
multiTabsSetting
});
}
...
...
@@ -21,5 +23,6 @@ export function useMultipleTabSetting() {
getMultipleTabSetting
,
getShowMultipleTab
,
getShowQuick
,
getShowRedo
,
};
}
src/hooks/web/useAppInject.ts
0 → 100644
浏览文件 @
a65ad9ed
import
{
useAppProviderContext
}
from
'
/@/components/Application
'
;
import
{
computed
,
unref
}
from
'
vue
'
;
export
function
useAppInject
()
{
const
values
=
useAppProviderContext
();
return
{
getIsMobile
:
computed
(()
=>
unref
(
values
.
isMobile
)),
};
}
src/hooks/web/useI18n.ts
浏览文件 @
a65ad9ed
...
...
@@ -25,7 +25,7 @@ export function useI18n(namespace?: string) {
return
{
...
methods
,
t
:
(
key
:
string
,
...
arg
:
Partial
<
Parameters
<
typeof
t
>>
)
=>
{
t
:
(
key
:
string
,
...
arg
:
any
)
=>
{
if
(
!
key
)
return
''
;
return
t
(
getKey
(
key
),
...(
arg
as
Parameters
<
typeof
t
>
));
},
...
...
src/hooks/web/usePage.ts
浏览文件 @
a65ad9ed
...
...
@@ -33,11 +33,13 @@ export function useGo() {
export
const
useRedo
=
()
=>
{
const
{
push
,
currentRoute
}
=
router
;
const
{
query
,
params
}
=
currentRoute
.
value
;
function
redo
()
{
push
({
path
:
'
/redirect
'
+
unref
(
currentRoute
).
fullPath
,
query
,
params
,
function
redo
():
Promise
<
boolean
>
{
return
new
Promise
((
resolve
)
=>
{
push
({
path
:
'
/redirect
'
+
unref
(
currentRoute
).
fullPath
,
query
,
params
,
}).
then
(()
=>
resolve
(
true
));
});
}
return
redo
;
...
...
src/hooks/web/useTabs.ts
浏览文件 @
a65ad9ed
...
...
@@ -11,7 +11,11 @@ export function useTabs() {
}
return
{
refreshPage
:
()
=>
canIUseFn
()
&&
tabStore
.
commitRedoPage
(),
refreshPage
:
async
()
=>
{
if
(
canIUseFn
())
{
await
tabStore
.
commitRedoPage
();
}
},
closeAll
:
()
=>
canIUseFn
()
&&
tabStore
.
closeAllTabAction
(),
closeLeft
:
()
=>
canIUseFn
()
&&
tabStore
.
closeLeftTabAction
(
tabStore
.
getCurrentTab
),
closeRight
:
()
=>
canIUseFn
()
&&
tabStore
.
closeRightTabAction
(
tabStore
.
getCurrentTab
),
...
...
src/layouts/default/content/index.vue
浏览文件 @
a65ad9ed
...
...
@@ -20,11 +20,10 @@
import
{
useTransitionSetting
}
from
'
/@/hooks/setting/useTransitionSetting
'
;
import
PageLayout
from
'
/@/layouts/page/index
'
;
import
{
Loading
}
from
'
/@/components/Loading
'
;
import
Transition
from
'
/@/views/demo/comp/lazy/Transition.vue
'
;
export
default
defineComponent
({
name
:
'
LayoutContent
'
,
components
:
{
PageLayout
,
Loading
,
Transition
},
components
:
{
PageLayout
,
Loading
},
setup
()
{
const
{
prefixCls
}
=
useDesign
(
'
layout-content
'
);
const
{
getOpenPageLoading
}
=
useTransitionSetting
();
...
...
src/layouts/default/header/LayoutHeader.tsx
浏览文件 @
a65ad9ed
...
...
@@ -3,38 +3,19 @@ import './index.less';
import
type
{
FunctionalComponent
}
from
'
vue
'
;
import
type
{
Component
}
from
'
/@/components/types
'
;
import
{
defineComponent
,
unref
,
computed
,
ref
,
nextTick
,
watchEffect
,
// nextTick
}
from
'
vue
'
;
import
{
defineComponent
,
unref
,
computed
}
from
'
vue
'
;
import
{
Layout
,
Tooltip
,
Badge
}
from
'
ant-design-vue
'
;
import
{
AppLogo
}
from
'
/@/components/Application
'
;
import
UserDropdown
from
'
./UserDropdown
'
;
import
LayoutMenu
from
'
../menu
'
;
import
LayoutBreadcrumb
from
'
./LayoutBreadcrumb.vue
'
;
import
LockAction
from
'
./actions/LockAction
'
;
import
LayoutTrigger
from
'
../trigger/index.vue
'
;
import
NoticeAction
from
'
./notice/NoticeActionItem.vue
'
;
import
{
RedoOutlined
,
FullscreenExitOutlined
,
FullscreenOutlined
,
LockOutlined
,
BugOutlined
,
}
from
'
@ant-design/icons-vue
'
;
import
{
LockOutlined
,
BugOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
AppSearch
}
from
'
/@/components/Application
'
;
import
{
useModal
}
from
'
/@/components/Modal
'
;
import
{
useFullscreen
}
from
'
/@/hooks/web/useFullScreen
'
;
import
{
useTabs
}
from
'
/@/hooks/web/useTabs
'
;
import
{
useWindowSizeFn
}
from
'
/@/hooks/event/useWindowSizeFn
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
useRootSetting
}
from
'
/@/hooks/setting/useRootSetting
'
;
...
...
@@ -49,8 +30,10 @@ import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
import
{
AppLocalePicker
}
from
'
/@/components/Application
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
import
{
useLayoutContext
}
from
'
../useLayoutContext
'
;
import
{
UserDropDown
,
LayoutBreadcrumb
,
FullScreen
}
from
'
./components
'
;
import
{
useAppInject
}
from
'
/@/hooks/web/useAppInject
'
;
import
{
useDesign
}
from
'
../../../hooks/web/useDesign
'
;
interface
TooltipItemProps
{
title
:
string
;
}
...
...
@@ -72,24 +55,14 @@ export default defineComponent({
fixed
:
propTypes
.
bool
,
},
setup
(
props
)
{
let
logoEl
:
Element
|
null
|
undefined
;
const
logoWidthRef
=
ref
(
200
);
const
logoRef
=
ref
<
ComponentRef
>
(
null
);
const
injectValue
=
useLayoutContext
();
const
{
refreshPage
}
=
useTabs
();
const
{
t
}
=
useI18n
();
const
{
getShowTopMenu
,
getShowHeaderTrigger
,
getSplit
,
getIsHorizontal
}
=
useMenuSetting
();
const
{
prefixCls
}
=
useDesign
(
'
layout-header
'
);
const
{
getShowTopMenu
,
getShowHeaderTrigger
,
getSplit
}
=
useMenuSetting
();
const
{
getShowLocale
}
=
useLocaleSetting
();
const
{
getUseErrorHandle
,
getShowBreadCrumbIcon
}
=
useRootSetting
();
const
{
getUseErrorHandle
}
=
useRootSetting
();
const
{
getHeaderTheme
,
getShowRedo
,
getUseLockPage
,
getShowFullScreen
,
getShowNotice
,
...
...
@@ -100,23 +73,11 @@ export default defineComponent({
const
{
push
}
=
useRouter
();
const
[
register
,
{
openModal
}]
=
useModal
();
const
{
toggleFullscreen
,
isFullscreenRef
}
=
useFullscreen
();
useWindowSizeFn
(
()
=>
{
calcTopMenuWidth
();
},
100
,
{
immediate
:
false
}
);
const
{
getIsMobile
}
=
useAppInject
();
const
headerClass
=
computed
(()
=>
{
const
theme
=
unref
(
getHeaderTheme
);
return
theme
?
`layout-header__header--
${
theme
}
`
:
''
;
});
const
isPc
=
computed
(()
=>
{
return
!
unref
(
injectValue
.
isMobile
);
return
theme
?
`
${
prefixCls
}
__header--
${
theme
}
`
:
''
;
});
const
getSplitType
=
computed
(()
=>
{
...
...
@@ -127,25 +88,6 @@ export default defineComponent({
return
unref
(
getSplit
)
?
MenuModeEnum
.
HORIZONTAL
:
null
;
});
watchEffect
(()
=>
{
if
(
unref
(
getIsHorizontal
))
{
calcTopMenuWidth
();
}
});
function
calcTopMenuWidth
()
{
nextTick
(()
=>
{
if
(
!
unref
(
getShowTopMenu
))
return
;
let
width
=
0
;
if
(
!
logoEl
)
{
logoEl
=
unref
(
logoRef
)?.
$el
;
}
if
(
!
logoEl
)
return
;
width
+=
logoEl
.
clientWidth
;
logoWidthRef
.
value
=
width
+
80
;
});
}
function
handleToErrorList
()
{
push
(
PageEnum
.
ERROR_LOG_PAGE
).
then
(()
=>
{
errorStore
.
commitErrorListCountState
(
0
);
...
...
@@ -156,27 +98,28 @@ export default defineComponent({
openModal
(
true
);
}
function
renderHeaderContent
()
{
const
width
=
unref
(
logoWidthRef
);
function
renderHeaderLeft
()
{
return
(
<
div
class
=
"layout-header__content "
>
{
unref
(
getShowHeaderLogo
)
&&
(
<
AppLogo
class
=
{
`layout-header__logo`
}
ref
=
{
logoRef
}
theme
=
{
unref
(
getHeaderTheme
)
}
/>
)
}
<>
{
unref
(
getShowContent
)
&&
(
<
div
class
=
"layout-header__left"
>
<
div
class
=
{
`
${
prefixCls
}
__left`
}
>
{
unref
(
getShowHeaderTrigger
)
&&
(
<
LayoutTrigger
theme
=
{
unref
(
getHeaderTheme
)
}
sider
=
{
false
}
/>
)
}
{
unref
(
getShowBread
)
&&
unref
(
isPc
)
&&
(
<
LayoutBreadcrumb
showIcon
=
{
unref
(
getShowBreadCrumbIcon
)
}
/>
{
unref
(
getShowBread
)
&&
!
unref
(
getIsMobile
)
&&
(
<
LayoutBreadcrumb
theme
=
{
unref
(
getHeaderTheme
)
}
/>
)
}
</
div
>
)
}
</>
);
}
{
unref
(
getShowTopMenu
)
&&
unref
(
isPc
)
&&
(
<
div
class
=
{
[
`layout-header__menu `
]
}
style
=
{
{
width
:
`calc(100% -
${
width
}
px)`
}
}
>
function
renderHeaderContent
()
{
return
(
<
div
class
=
{
`
${
prefixCls
}
__content`
}
>
{
unref
(
getShowTopMenu
)
&&
!
unref
(
getIsMobile
)
&&
(
<
div
class
=
{
[
`
${
prefixCls
}
__menu `
]
}
>
{
/* <div class={[`layout-header__menu `]}> */
}
<
LayoutMenu
isHorizontal
=
{
true
}
...
...
@@ -193,18 +136,18 @@ export default defineComponent({
function
renderActionDefault
(
Comp
:
Component
|
any
,
event
:
Fn
)
{
return
(
<
div
class
=
"layout-header__action-item"
onClick
=
{
event
}
>
<
Comp
class
=
"layout-header__action-icon"
/>
<
div
class
=
{
`
${
prefixCls
}
__action-item`
}
onClick
=
{
event
}
>
<
Comp
class
=
{
`
${
prefixCls
}
__action-icon`
}
/>
</
div
>
);
}
function
renderAction
()
{
return
(
<
div
class
=
{
`
layout-header
__action`
}
>
{
unref
(
isPc
)
&&
<
AppSearch
class
=
"layout-header__action-item"
/>
}
<
div
class
=
{
`
${
prefixCls
}
__action`
}
>
{
!
unref
(
getIsMobile
)
&&
<
AppSearch
class
=
{
`
${
prefixCls
}
__action-item`
}
/>
}
{
unref
(
getUseErrorHandle
)
&&
unref
(
isPc
)
&&
(
{
unref
(
getUseErrorHandle
)
&&
!
unref
(
getIsMobile
)
&&
(
<
TooltipItem
title
=
{
t
(
'
layout.header.tooltipErrorLog
'
)
}
>
{
()
=>
(
<
Badge
...
...
@@ -219,48 +162,27 @@ export default defineComponent({
</
TooltipItem
>
)
}
{
unref
(
getUseLockPage
)
&&
unref
(
isPc
)
&&
(
{
unref
(
getUseLockPage
)
&&
!
unref
(
getIsMobile
)
&&
(
<
TooltipItem
title
=
{
t
(
'
layout.header.tooltipLock
'
)
}
>
{
()
=>
renderActionDefault
(
LockOutlined
,
handleLockPage
)
}
</
TooltipItem
>
)
}
{
unref
(
getShowNotice
)
&&
unref
(
isPc
)
&&
(
{
unref
(
getShowNotice
)
&&
!
unref
(
getIsMobile
)
&&
(
<
TooltipItem
title
=
{
t
(
'
layout.header.tooltipNotify
'
)
}
>
{
()
=>
<
NoticeAction
/>
}
</
TooltipItem
>
)
}
{
unref
(
getShowRedo
)
&&
unref
(
isPc
)
&&
(
<
TooltipItem
title
=
{
t
(
'
layout.header.tooltipRedo
'
)
}
>
{
()
=>
renderActionDefault
(
RedoOutlined
,
refreshPage
)
}
</
TooltipItem
>
)
}
{
unref
(
getShowFullScreen
)
&&
!
unref
(
getIsMobile
)
&&
<
FullScreen
/>
}
<
UserDropDown
theme
=
{
unref
(
getHeaderTheme
)
}
/>
{
unref
(
getShowFullScreen
)
&&
unref
(
isPc
)
&&
(
<
TooltipItem
title
=
{
unref
(
isFullscreenRef
)
?
t
(
'
layout.header.tooltipExitFull
'
)
:
t
(
'
layout.header.tooltipEntryFull
'
)
}
>
{
()
=>
{
const
Icon
=
!
unref
(
isFullscreenRef
)
?
(
<
FullscreenOutlined
/>
)
:
(
<
FullscreenExitOutlined
/>
);
return
renderActionDefault
(
Icon
,
toggleFullscreen
);
}
}
</
TooltipItem
>
)
}
<
UserDropdown
class
=
"layout-header__user-dropdown"
/>
{
unref
(
getShowLocale
)
&&
(
<
AppLocalePicker
reload
=
{
true
}
showText
=
{
false
}
class
=
"layout-header__action-item locale"
class
=
{
`
${
prefixCls
}
__action-item locale`
}
/>
)
}
</
div
>
...
...
@@ -270,6 +192,10 @@ export default defineComponent({
function
renderHeaderDefault
()
{
return
(
<>
{
unref
(
getShowHeaderLogo
)
&&
(
<
AppLogo
class
=
{
`
${
prefixCls
}
__logo`
}
theme
=
{
unref
(
getHeaderTheme
)
}
/>
)
}
{
renderHeaderLeft
()
}
{
renderHeaderContent
()
}
{
renderAction
()
}
<
LockAction
onRegister
=
{
register
}
/>
...
...
@@ -279,9 +205,7 @@ export default defineComponent({
return
()
=>
{
return
(
<
Layout
.
Header
class
=
{
[
'
layout-header
'
,
'
flex p-0 px-4
'
,
unref
(
headerClass
),
{
fixed
:
props
.
fixed
}]
}
>
<
Layout
.
Header
class
=
{
[
prefixCls
,
unref
(
headerClass
),
{
fixed
:
props
.
fixed
}]
}
>
{
()
=>
renderHeaderDefault
()
}
</
Layout
.
Header
>
);
...
...
src/layouts/default/header/LayoutMultipleHeader.less
浏览文件 @
a65ad9ed
@import (reference) '../../../design/index.less';
.multiple-tab-header {
margin-left: 1px;
transition: width 0.2s;
...
...
@@ -10,7 +12,7 @@
&.fixed {
position: fixed;
top: 0;
z-index:
100
;
z-index:
@multiple-tab-fixed-z-index
;
width: 100%;
}
}
src/layouts/default/header/LayoutMultipleHeader.tsx
浏览文件 @
a65ad9ed
...
...
@@ -2,7 +2,7 @@ import './LayoutMultipleHeader.less';
import
{
defineComponent
,
unref
,
computed
,
ref
,
watch
,
nextTick
,
CSSProperties
}
from
'
vue
'
;
import
LayoutHeader
from
'
./
LayoutHeader
'
;
import
LayoutHeader
from
'
./
index.vue
'
;
import
MultipleTabs
from
'
../tabs/index.vue
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
...
...
@@ -10,6 +10,7 @@ import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import
{
useFullContent
}
from
'
/@/hooks/web/useFullContent
'
;
import
{
useMultipleTabSetting
}
from
'
/@/hooks/setting/useMultipleTabSetting
'
;
import
{
useLayoutContext
}
from
'
../useLayoutContext
'
;
import
{
useAppInject
}
from
'
/@/hooks/web/useAppInject
'
;
export
default
defineComponent
({
name
:
'
LayoutMultipleHeader
'
,
...
...
@@ -21,8 +22,8 @@ export default defineComponent({
const
injectValue
=
useLayoutContext
();
const
{
getCalcContentWidth
}
=
useMenuSetting
();
const
{
getCalcContentWidth
,
getSplit
}
=
useMenuSetting
();
const
{
getIsMobile
}
=
useAppInject
();
const
{
getFixed
,
getShowInsetHeaderRef
,
...
...
@@ -36,7 +37,7 @@ export default defineComponent({
const
{
getShowMultipleTab
}
=
useMultipleTabSetting
();
const
showTabsRef
=
computed
(()
=>
{
const
getShowTabs
=
computed
(()
=>
{
return
unref
(
getShowMultipleTab
)
&&
!
unref
(
getFullContent
);
});
...
...
@@ -56,7 +57,7 @@ export default defineComponent({
():
CSSProperties
=>
{
const
style
:
CSSProperties
=
{};
if
(
unref
(
getFixed
))
{
style
.
width
=
unref
(
injectValue
.
i
sMobile
)
?
'
100%
'
:
unref
(
getCalcContentWidth
);
style
.
width
=
unref
(
getI
sMobile
)
?
'
100%
'
:
unref
(
getCalcContentWidth
);
}
if
(
unref
(
getShowFullHeaderRef
))
{
style
.
top
=
`
${
unref
(
fullHeaderHeightRef
)}
px`
;
...
...
@@ -84,7 +85,7 @@ export default defineComponent({
const
fullHeaderEl
=
unref
(
injectValue
.
fullHeader
)?.
$el
;
let
height
=
0
;
if
(
headerEl
&&
!
unref
(
getShowFullHeaderRef
))
{
if
(
headerEl
&&
!
unref
(
getShowFullHeaderRef
)
&&
!
unref
(
getSplit
)
)
{
height
+=
headerEl
.
offsetHeight
;
}
...
...
@@ -97,6 +98,7 @@ export default defineComponent({
height
+=
fullHeaderHeight
;
fullHeaderHeightRef
.
value
=
fullHeaderHeight
;
}
placeholderHeightRef
.
value
=
height
;
});
},
...
...
@@ -114,7 +116,7 @@ export default defineComponent({
class
=
{
[
'
multiple-tab-header
'
,
unref
(
getHeaderTheme
),
{
fixed
:
unref
(
getIsFixed
)
}]
}
>
{
unref
(
getShowInsetHeaderRef
)
&&
<
LayoutHeader
ref
=
{
headerElRef
}
/>
}
{
unref
(
showTabsRef
)
&&
<
MultipleTabs
ref
=
{
tabElRef
}
/>
}
{
unref
(
getShowTabs
)
&&
<
MultipleTabs
ref
=
{
tabElRef
}
/>
}
</
div
>
</>
);
...
...
src/layouts/default/header/UserDropdown.tsx
已删除
100644 → 0
浏览文件 @
ec7efcf0
// components
import
{
Dropdown
,
Menu
}
from
'
ant-design-vue
'
;
import
{
defineComponent
,
computed
,
unref
}
from
'
vue
'
;
// res
import
headerImg
from
'
/@/assets/images/header.jpg
'
;
import
Icon
from
'
/@/components/Icon/index
'
;
import
{
userStore
}
from
'
/@/store/modules/user
'
;
import
{
DOC_URL
}
from
'
/@/settings/siteSetting
'
;
import
{
openWindow
}
from
'
/@/utils
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
FunctionalComponent
}
from
'
vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
type
MenuEvent
=
'
loginOut
'
|
'
doc
'
;
interface
MenuItemProps
{
icon
:
string
;
text
:
string
;
key
:
MenuEvent
;
}
const
prefixCls
=
'
user-dropdown
'
;
const
MenuItem
:
FunctionalComponent
<
MenuItemProps
>
=
(
props
)
=>
{
const
{
key
,
icon
,
text
}
=
props
;
return
(
<
Menu
.
Item
key
=
{
key
}
>
{
()
=>
(
<
span
class
=
"flex items-center"
>
<
Icon
icon
=
{
icon
}
class
=
"mr-1"
/>
<
span
>
{
text
}
</
span
>
</
span
>
)
}
</
Menu
.
Item
>
);
};
export
default
defineComponent
({
name
:
'
UserDropdown
'
,
setup
()
{
const
{
t
}
=
useI18n
();
const
{
getShowDoc
}
=
useHeaderSetting
();
const
getUserInfo
=
computed
(()
=>
{
const
{
realName
=
''
,
desc
}
=
userStore
.
getUserInfoState
||
{};
return
{
realName
,
desc
};
});
// login out
function
handleLoginOut
()
{
userStore
.
confirmLoginOut
();
}
// open doc
function
openDoc
()
{
openWindow
(
DOC_URL
);
}
function
handleMenuClick
(
e
:
{
key
:
MenuEvent
})
{
switch
(
e
.
key
)
{
case
'
loginOut
'
:
handleLoginOut
();
break
;
case
'
doc
'
:
openDoc
();
break
;
}
}
function
renderSlotsDefault
()
{
const
{
realName
}
=
unref
(
getUserInfo
);
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
&&
(
<
MenuItem
key
=
"doc"
text
=
{
t
(
'
layout.header.dropdownItemDoc
'
)
}
icon
=
"gg:loadbar-doc"
/>
)
}
{
/* @ts-ignore */
}
{
showDoc
&&
<
Menu
.
Divider
/>
}
<
MenuItem
key
=
"loginOut"
text
=
{
t
(
'
layout.header.dropdownItemLoginOut
'
)
}
icon
=
"carbon:power"
/>
</>
)
}
</
Menu
>
);
}
return
()
=>
{
return
(
<
Dropdown
placement
=
"bottomLeft"
overlayClassName
=
"app-layout-header-user-dropdown-overlay"
>
{
{
default
:
()
=>
renderSlotsDefault
(),
overlay
:
()
=>
renderSlotOverlay
(),
}
}
</
Dropdown
>
);
};
},
});
src/layouts/default/header/
Layout
Breadcrumb.vue
→
src/layouts/default/header/
components/
Breadcrumb.vue
浏览文件 @
a65ad9ed
<
template
>
<div
class=
"layout-breadcrumb
"
>
<div
:class=
"[prefixCls, `$
{prefixCls}--${theme}`]
">
<a-breadcrumb
:routes=
"routes"
>
<template
#itemRender
="
{ route, routes }">
<Icon
:icon=
"route.meta.icon"
v-if=
"
show
Icon && route.meta.icon"
/>
<Icon
:icon=
"route.meta.icon"
v-if=
"
getShowBreadCrumb
Icon && route.meta.icon"
/>
<span
v-if=
"routes.indexOf(route) === routes.length - 1"
>
{{
t
(
route
.
meta
.
title
)
}}
</span>
...
...
@@ -14,7 +14,6 @@
</div>
</template>
<
script
lang=
"ts"
>
import
{
PropType
}
from
'
vue
'
;
import
{
defineComponent
,
ref
,
toRaw
,
watchEffect
}
from
'
vue
'
;
import
{
useI18n
}
from
'
vue-i18n
'
;
...
...
@@ -26,18 +25,23 @@
import
{
HomeOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
PageEnum
}
from
'
/@/enums/pageEnum
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
useRootSetting
}
from
'
/@/hooks/setting/useRootSetting
'
;
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
export
default
defineComponent
({
name
:
'
LayoutBreadcrumb
'
,
components
:
{
HomeOutlined
,
Icon
},
props
:
{
showIcon
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
default
:
false
,
},
theme
:
propTypes
.
oneOf
([
'
dark
'
,
'
light
'
]),
},
setup
()
{
const
routes
=
ref
<
RouteLocationMatched
[]
>
([]);
const
{
currentRoute
}
=
useRouter
();
const
{
prefixCls
}
=
useDesign
(
'
layout-breadcrumb
'
);
const
{
getShowBreadCrumbIcon
}
=
useRootSetting
();
const
{
t
}
=
useI18n
();
watchEffect
(()
=>
{
...
...
@@ -63,17 +67,71 @@
);
if
(
filterBreadcrumbList
.
length
===
breadcrumbList
.
length
)
{
filterBreadcrumbList
.
unshift
({
filterBreadcrumbList
.
unshift
(
(
{
path
:
PageEnum
.
BASE_HOME
,
meta
:
{
title
:
t
(
'
layout.header.home
'
),
},
});
}
as
unknown
)
as
RouteLocationMatched
);
}
routes
.
value
=
filterBreadcrumbList
;
routes
.
value
=
filterBreadcrumbList
.
length
===
1
?
[]
:
filterBreadcrumbList
;
});
return
{
routes
,
t
};
return
{
routes
,
t
,
prefixCls
,
getShowBreadCrumbIcon
};
},
});
</
script
>
<
style
lang=
"less"
>
@import (reference) '../../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-breadcrumb';
.@{prefix-cls} {
display: flex;
padding: 0 8px;
align-items: center;
.ant-breadcrumb-link {
.anticon {
margin-right: 4px;
margin-bottom: 2px;
}
}
&--light {
.ant-breadcrumb-link {
color: @breadcrumb-item-normal-color;
a {
color: @text-color-base;
&:hover {
color: @primary-color;
}
}
}
.ant-breadcrumb-separator {
color: @breadcrumb-item-normal-color;
}
}
&--dark {
.ant-breadcrumb-link {
color: rgba(255, 255, 255, 0.6);
a {
color: rgba(255, 255, 255, 0.8);
&:hover {
color: @white;
}
}
}
.ant-breadcrumb-separator,
.anticon {
color: rgba(255, 255, 255, 0.8);
}
}
}
</
style
>
src/layouts/default/header/components/ErrorAction.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<Tooltip
:title=
"t('layout.header.tooltipErrorLog')"
placement=
"bottom"
:mouseEnterDelay=
"0.5"
@
click=
"handleToErrorList"
>
<Badge
:count=
"getCount"
:offset=
"[0, 10]"
dot
:overflowCount=
"99"
>
<BugOutlined
/>
</Badge>
</Tooltip>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
computed
}
from
'
vue
'
;
import
{
Tooltip
,
Badge
}
from
'
ant-design-vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
BugOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
errorStore
}
from
'
/@/store/modules/error
'
;
import
{
PageEnum
}
from
'
/@/enums/pageEnum
'
;
import
{
useRouter
}
from
'
vue-router
'
;
export
default
defineComponent
({
name
:
'
ErrorAction
'
,
components
:
{
BugOutlined
,
Tooltip
,
Badge
},
setup
()
{
const
{
t
}
=
useI18n
();
const
{
push
}
=
useRouter
();
const
getCount
=
computed
(()
=>
{
return
errorStore
.
getErrorListCountState
;
});
function
handleToErrorList
()
{
push
(
PageEnum
.
ERROR_LOG_PAGE
).
then
(()
=>
{
errorStore
.
commitErrorListCountState
(
0
);
});
}
return
{
t
,
getCount
,
handleToErrorList
,
};
},
});
</
script
>
src/layouts/default/header/components/FullScreen.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<Tooltip
:title=
"getTitle"
placement=
"bottom"
:mouseEnterDelay=
"0.5"
>
<span
@
click=
"toggleFullscreen"
>
<FullscreenOutlined
v-if=
"!isFullscreen"
/>
<FullscreenExitOutlined
v-else
/>
</span>
</Tooltip>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
computed
,
unref
}
from
'
vue
'
;
import
{
Tooltip
}
from
'
ant-design-vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useFullscreen
}
from
'
/@/hooks/web/useFullScreen
'
;
import
{
FullscreenExitOutlined
,
FullscreenOutlined
}
from
'
@ant-design/icons-vue
'
;
export
default
defineComponent
({
name
:
'
FullScreen
'
,
components
:
{
FullscreenExitOutlined
,
FullscreenOutlined
,
Tooltip
},
setup
()
{
const
{
t
}
=
useI18n
();
const
{
toggleFullscreen
,
isFullscreenRef
}
=
useFullscreen
();
const
getTitle
=
computed
(()
=>
{
return
unref
(
isFullscreenRef
)
?
t
(
'
layout.header.tooltipExitFull
'
)
:
t
(
'
layout.header.tooltipEntryFull
'
);
});
return
{
getTitle
,
isFullscreen
:
isFullscreenRef
,
toggleFullscreen
,
};
},
});
</
script
>
src/layouts/default/header/components/index.ts
0 → 100644
浏览文件 @
a65ad9ed
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
export
const
UserDropDown
=
createAsyncComponent
(()
=>
import
(
'
./user-dropdown/index.vue
'
),
{
loading
:
true
,
});
export
const
LayoutBreadcrumb
=
createAsyncComponent
(()
=>
import
(
'
./Breadcrumb.vue
'
));
export
const
FullScreen
=
createAsyncComponent
(()
=>
import
(
'
./FullScreen.vue
'
));
export
const
Notify
=
createAsyncComponent
(()
=>
import
(
'
./notify/index.vue
'
));
export
const
LockItem
=
createAsyncComponent
(()
=>
import
(
'
./lock/index.vue
'
));
export
const
ErrorAction
=
createAsyncComponent
(()
=>
import
(
'
./ErrorAction.vue
'
));
src/layouts/default/header/
actions
/LockAction.less
→
src/layouts/default/header/
components/lock
/LockAction.less
浏览文件 @
a65ad9ed
文件已移动
src/layouts/default/header/
actions
/LockAction.tsx
→
src/layouts/default/header/
components/lock
/LockAction.tsx
浏览文件 @
a65ad9ed
文件已移动
src/layouts/default/header/components/lock/LockModal.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<BasicModal
:footer=
"null"
:title=
"t('layout.header.lockScreen')"
v-bind=
"$attrs"
:class=
"prefixCls"
@
register=
"register"
>
<div
:class=
"`$
{prefixCls}__entry`">
<div
:class=
"`$
{prefixCls}__header`">
<img
src=
"/@/assets/images/header.jpg"
:class=
"`$
{prefixCls}__header-img`" />
<p
:class=
"`$
{prefixCls}__header-name`">
{{
getRealName
}}
</p>
</div>
<BasicForm
@
register=
"registerForm"
layout=
"vertical"
/>
<div
:class=
"`$
{prefixCls}__footer`">
<a-button
type=
"primary"
block
class=
"mt-2"
@
click=
"handleLock"
>
{{
t
(
'
layout.header.lockScreenBtn
'
)
}}
</a-button>
</div>
</div>
</BasicModal>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
computed
}
from
'
vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
BasicModal
,
useModalInner
}
from
'
/@/components/Modal/index
'
;
import
{
BasicForm
,
useForm
}
from
'
/@/components/Form/index
'
;
import
{
userStore
}
from
'
/@/store/modules/user
'
;
import
{
lockStore
}
from
'
/@/store/modules/lock
'
;
export
default
defineComponent
({
name
:
'
LockModal
'
,
components
:
{
BasicModal
,
BasicForm
},
setup
()
{
const
{
t
}
=
useI18n
();
const
{
prefixCls
}
=
useDesign
(
'
header-lock-modal
'
);
const
getRealName
=
computed
(()
=>
{
return
userStore
.
getUserInfoState
?.
realName
;
});
const
[
register
,
{
closeModal
}]
=
useModalInner
();
const
[
registerForm
,
{
validateFields
,
resetFields
}]
=
useForm
({
showActionButtonGroup
:
false
,
schemas
:
[
{
field
:
'
password
'
,
label
:
t
(
'
layout.header.lockScreenPassword
'
),
component
:
'
InputPassword
'
,
required
:
true
,
},
],
});
async
function
handleLock
()
{
const
values
=
(
await
validateFields
())
as
any
;
const
password
:
string
|
undefined
=
values
.
password
;
closeModal
();
lockStore
.
commitLockInfoState
({
isLock
:
true
,
pwd
:
password
,
});
await
resetFields
();
}
return
{
t
,
prefixCls
,
getRealName
,
register
,
registerForm
,
handleLock
,
};
},
});
</
script
>
<
style
lang=
"less"
>
@import (reference) '../../../../../design/index.less';
@prefix-cls: ~'@{namespace}-header-lock-modal';
.@{prefix-cls} {
&__entry {
position: relative;
height: 240px;
padding: 130px 30px 60px 30px;
background: #fff;
border-radius: 10px;
}
&__header {
position: absolute;
top: 0;
left: calc(50% - 45px);
width: auto;
text-align: center;
&-img {
width: 70px;
border-radius: 50%;
}
&-name {
margin-top: 5px;
}
}
&__footer {
text-align: center;
}
}
</
style
>
src/layouts/default/header/components/lock/index.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<span
@
click=
"handleLock"
>
<Tooltip
:title=
"t('layout.header.tooltipLock')"
placement=
"bottom"
:mouseEnterDelay=
"0.5"
>
<LockOutlined
/>
</Tooltip>
<LockAction
@
register=
"register"
/>
</span>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
}
from
'
vue
'
;
import
{
Tooltip
}
from
'
ant-design-vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
LockOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
useModal
}
from
'
/@/components/Modal
'
;
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
export
default
defineComponent
({
name
:
'
FullScreen
'
,
components
:
{
LockOutlined
,
Tooltip
,
LockAction
:
createAsyncComponent
(()
=>
import
(
'
./LockModal.vue
'
)),
},
setup
()
{
const
{
t
}
=
useI18n
();
const
[
register
,
{
openModal
}]
=
useModal
();
function
handleLock
()
{
openModal
(
true
);
}
return
{
t
,
register
,
handleLock
,
};
},
});
</
script
>
src/layouts/default/header/
notice
/NoticeList.vue
→
src/layouts/default/header/
components/notify
/NoticeList.vue
浏览文件 @
a65ad9ed
<
template
>
<a-list
class=
"list
"
>
<a-list
:class=
"prefixCls
"
>
<template
v-for=
"item in list"
:key=
"item.id"
>
<a-list-item
class=
"list-item"
>
<a-list-item-meta>
...
...
@@ -33,6 +33,7 @@
<
script
lang=
"ts"
>
import
{
defineComponent
,
PropType
}
from
'
vue
'
;
import
{
ListItem
}
from
'
./data
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
export
default
defineComponent
({
props
:
{
...
...
@@ -41,10 +42,17 @@
default
:
()
=>
[],
},
},
setup
()
{
const
{
prefixCls
}
=
useDesign
(
'
header-notify-list
'
);
return
{
prefixCls
};
},
});
</
script
>
<
style
lang=
"less"
scoped
>
.list {
@import (reference) '../../../../../design/index.less';
@prefix-cls: ~'@{namespace}-header-notify-list';
.@{prefix-cls} {
&::-webkit-scrollbar {
display: none;
}
...
...
src/layouts/default/header/
notice
/data.ts
→
src/layouts/default/header/
components/notify
/data.ts
浏览文件 @
a65ad9ed
文件已移动
src/layouts/default/header/
notice/NoticeActionItem
.vue
→
src/layouts/default/header/
components/notify/index
.vue
浏览文件 @
a65ad9ed
<
template
>
<div
class=
"layout-header__action-item notify-action
"
>
<Popover
title=
""
trigger=
"click"
overlayClassName=
"layout-header__notify-action
"
>
<div
:class=
"prefixCls
"
>
<Popover
title=
""
trigger=
"click"
:overlayClassName=
"`$
{prefixCls}__overlay`
">
<Badge
:count=
"count"
dot
:numberStyle=
"numberStyle"
>
<BellOutlined
class=
"layout-header__action-icon"
/>
<BellOutlined
/>
</Badge>
<template
#content
>
<Tabs>
...
...
@@ -26,10 +26,13 @@
import
{
BellOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
tabListData
}
from
'
./data
'
;
import
NoticeList
from
'
./NoticeList.vue
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
export
default
defineComponent
({
components
:
{
Popover
,
BellOutlined
,
Tabs
,
TabPane
:
Tabs
.
TabPane
,
Badge
,
NoticeList
},
setup
()
{
const
{
prefixCls
}
=
useDesign
(
'
header-notify
'
);
let
count
=
0
;
for
(
let
i
=
0
;
i
<
tabListData
.
length
;
i
++
)
{
...
...
@@ -37,6 +40,7 @@
}
return
{
prefixCls
,
tabListData
,
count
,
numberStyle
:
{},
...
...
@@ -45,13 +49,16 @@
});
</
script
>
<
style
lang=
"less"
>
.layout-header__notify-action {
max-width: 360px;
}
@import (reference) '../../../../../design/index.less';
@prefix-cls: ~'@{namespace}-header-notify';
.
notify-action
{
.
@{prefix-cls}
{
padding-top: 2px;
&__overlay {
max-width: 360px;
}
.ant-tabs-content {
width: 300px;
}
...
...
src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<MenuItem
:key=
"key"
>
<span
class=
"flex items-center"
>
<Icon
:icon=
"icon"
class=
"mr-1"
/>
<span>
{{
text
}}
</span>
</span>
</MenuItem>
</
template
>
<
script
lang=
"ts"
>
// components
import
{
Menu
}
from
'
ant-design-vue
'
;
import
{
defineComponent
}
from
'
vue
'
;
import
Icon
from
'
/@/components/Icon/index
'
;
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
export
default
defineComponent
({
name
:
'
DropdownMenuItem
'
,
components
:
{
MenuItem
:
Menu
.
Item
,
Icon
},
props
:
{
key
:
propTypes
.
string
,
text
:
propTypes
.
string
,
icon
:
propTypes
.
string
,
},
});
</
script
>
src/layouts/default/header/components/user-dropdown/index.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<Dropdown
placement=
"bottomLeft"
:overlayClassName=
"`$
{prefixCls}-dropdown-overlay`">
<span
:class=
"[prefixCls, `$
{prefixCls}--${theme}`]">
<img
:class=
"`$
{prefixCls}__header`" src="/@/assets/images/header.jpg" />
<span
:class=
"`$
{prefixCls}__info`">
<span
:class=
"`$
{prefixCls}__name anticon`">
{{
getUserInfo
.
realName
}}
</span>
</span>
</span>
<template
#overlay
>
<Menu
@
click=
"handleMenuClick"
>
<MenuItem
key=
"doc"
:text=
"t('layout.header.dropdownItemDoc')"
icon=
"gg:loadbar-doc"
/>
<MenuDivider
v-if=
"getShowDoc"
/>
<MenuItem
key=
"loginOut"
:text=
"t('layout.header.dropdownItemLoginOut')"
icon=
"carbon:power"
/>
</Menu>
</
template
>
</Dropdown>
</template>
<
script
lang=
"ts"
>
// components
import
{
Dropdown
,
Menu
}
from
'
ant-design-vue
'
;
import
{
defineComponent
,
computed
}
from
'
vue
'
;
// res
import
Icon
from
'
/@/components/Icon/index
'
;
import
{
userStore
}
from
'
/@/store/modules/user
'
;
import
{
DOC_URL
}
from
'
/@/settings/siteSetting
'
;
import
{
openWindow
}
from
'
/@/utils
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
type
MenuEvent
=
'
loginOut
'
|
'
doc
'
;
export
default
defineComponent
({
name
:
'
UserDropdown
'
,
components
:
{
Dropdown
,
Menu
,
MenuItem
:
createAsyncComponent
(()
=>
import
(
'
./DropMenuItem.vue
'
)),
MenuDivider
:
Menu
.
Divider
,
Icon
,
},
props
:
{
theme
:
propTypes
.
oneOf
([
'
dark
'
,
'
light
'
]),
},
setup
()
{
const
{
prefixCls
}
=
useDesign
(
'
header-user-dropdown
'
);
const
{
t
}
=
useI18n
();
const
{
getShowDoc
}
=
useHeaderSetting
();
const
getUserInfo
=
computed
(()
=>
{
const
{
realName
=
''
,
desc
}
=
userStore
.
getUserInfoState
||
{};
return
{
realName
,
desc
};
});
// login out
function
handleLoginOut
()
{
userStore
.
confirmLoginOut
();
}
// open doc
function
openDoc
()
{
openWindow
(
DOC_URL
);
}
function
handleMenuClick
(
e
:
{
key
:
MenuEvent
})
{
switch
(
e
.
key
)
{
case
'
loginOut
'
:
handleLoginOut
();
break
;
case
'
doc
'
:
openDoc
();
break
;
}
}
return
{
prefixCls
,
t
,
getUserInfo
,
handleMenuClick
,
getShowDoc
,
};
},
});
</
script
>
<
style
lang=
"less"
>
@import (reference) '../../../../../design/index.less';
@prefix-cls: ~'@{namespace}-header-user-dropdown';
.@{prefix-cls} {
display: flex;
height: @header-height;
min-width: 100px;
padding: 0 0 0 10px;
padding-right: 10px;
overflow: hidden;
font-size: 12px;
cursor: pointer;
align-items: center;
&:hover {
background: @header-light-bg-hover-color;
}
img {
width: 26px;
height: 26px;
margin-right: 12px;
}
&__header {
border-radius: 50%;
}
&__name {
font-size: 14px;
}
&--dark {
&:hover {
background: @header-dark-bg-hover-color;
}
}
&--light {
.@{prefix-cls}__name {
color: @text-color-base;
}
.@{prefix-cls}__desc {
color: @header-light-desc-color;
}
}
&-dropdown-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
}
</
style
>
src/layouts/default/header/index.less
浏览文件 @
a65ad9ed
@import (reference) '../../../design/index.less';
@header-trigger-prefix-cls: ~'@{namespace}-layout-header-trigger';
@header-prefix-cls: ~'@{namespace}-layout-header';
@locale-prefix-cls: ~'@{namespace}-app-locale-picker';
.
layout-header
{
.
@{header-prefix-cls}
{
display: flex;
height: @header-height;
padding: 0
20px 0 0
;
padding: 0;
margin-left: -1px;
line-height: @header-height;
color: @white;
...
...
@@ -12,15 +14,28 @@
align-items: center;
justify-content: space-between;
&
.
fixed {
&
--
fixed {
position: fixed;
top: 0;
left: 0;
z-index:
1000
;
z-index:
@layout-header-fixed-z-index
;
width: 100%;
}
&__left {
&-logo {
height: @header-height;
min-width: 192px;
padding: 0 10px;
font-size: 14px;
img {
width: @logo-width;
height: @logo-width;
margin-right: 2px;
}
}
&-left {
display: flex;
height: 100%;
align-items: center;
...
...
@@ -33,7 +48,7 @@
align-items: center;
.anticon {
font-size: 1
7
px;
font-size: 1
6
px;
}
&.light {
...
...
@@ -52,82 +67,65 @@
}
}
}
.layout-breadcrumb {
display: flex;
padding: 0 8px;
align-items: center;
.ant-breadcrumb-link {
.anticon {
margin-right: 4px;
margin-bottom: 2px;
}
}
}
}
&__content {
display: flex;
&-menu {
height: 100%;
flex-grow: 1;
min-width: 0;
flex: 1;
align-items: center;
}
&__header--light {
background: @white;
border-bottom: 1px solid @header-light-bottom-border-color;
&-action {
display: flex;
min-width: 200px;
padding-right: 12px;
align-items: center;
.layout-header__menu {
height: calc(@header-height - 1px);
&__item {
display: flex;
height: @header-height;
padding: 0 2px;
font-size: 1.2em;
cursor: pointer;
align-items: center;
.ant-
menu-submenu
{
.ant-
badge
{
height: @header-height;
line-height: @header-height;
}
}
.layout-breadcrumb {
.ant-breadcrumb-link {
color: @breadcrumb-item-normal-color;
a {
color: @text-color-base;
&:hover {
color: @primary-color;
}
}
.ant-badge-dot {
top: 10px;
right: 2px;
}
}
.ant-breadcrumb-separator {
color: @breadcrumb-item-normal-color;
}
span[role='img'] {
padding: 0 8px;
}
}
.layout-header__logo
{
height: @header-height
;
color: @text-color-base
;
&--light
{
background: @white
;
border-bottom: 1px solid @header-light-bottom-border-color
;
img {
width: @logo-width;
height: @logo-width;
margin-right: 6px;
}
.@{header-prefix-cls}-logo {
color: @text-color-base;
&:hover {
background: @header-light-bg-hover-color;
}
}
.
layout-header__
action {
&
-
item {
.
@{header-prefix-cls}-
action {
&
__
item {
&:hover {
background: @header-light-bg-hover-color;
}
&.locale
{
padding: 0
10
px;
.@{locale-prefix-cls}
{
padding: 0
6
px;
color: rgba(0, 0, 0, 0.65);
}
}
...
...
@@ -137,134 +135,23 @@
color: @text-color-base;
}
}
.layout-header__user-dropdown {
&:hover {
background: @header-light-bg-hover-color;
}
}
.user-dropdown {
&__name {
color: @text-color-base;
}
&__desc {
color: @header-light-desc-color;
}
}
}
&
__header
--dark {
&--dark {
background: @header-dark-bg-color;
.layout-header__action {
&-item {
&:hover {
background: @header-dark-bg-hover-color;
}
}
}
.layout-header__logo {
height: @header-height;
img {
width: @logo-width;
height: @logo-width;
margin-right: 10px;
}
&:hover {
background: @header-dark-bg-hover-color;
}
}
.layout-header__user-dropdown {
.@{header-prefix-cls}-logo {
&:hover {
background: @header-dark-bg-hover-color;
}
}
.layout-breadcrumb {
.ant-breadcrumb-link {
color: rgba(255, 255, 255, 0.6);
a {
color: rgba(255, 255, 255, 0.8);
&:hover {
color: @white;
}
.@{header-prefix-cls}-action {
&__item {
&:hover {
background: @header-dark-bg-hover-color;
}
}
.ant-breadcrumb-separator,
.anticon {
color: rgba(255, 255, 255, 0.8);
}
}
}
&__logo {
padding: 0 10px;
}
&__bread {
display: none;
flex: 1;
}
&__action {
display: flex;
align-items: center;
&-item {
display: flex;
height: @header-height;
padding: 0 2px;
font-size: 1.2em;
cursor: pointer;
align-items: center;
}
&-icon {
padding: 0 8px;
}
}
&__menu {
margin-left: 4px;
overflow: hidden;
align-items: center;
}
&__user-dropdown {
height: @header-height;
padding: 0 0 0 10px;
}
.user-dropdown {
display: flex;
padding-right: 10px;
font-size: 12px;
cursor: pointer;
align-items: center;
img {
width: 26px;
height: 26px;
margin-right: 12px;
}
&__header {
border-radius: 50%;
}
}
}
.app-layout-header-user-dropdown-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
}
}
src/layouts/default/header/index.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<Header
:class=
"getHeaderClass"
>
<!-- left start -->
<div
:class=
"`$
{prefixCls}-left`">
<!-- logo -->
<AppLogo
v-if=
"getShowHeaderLogo"
:class=
"`$
{prefixCls}-logo`" :theme="getHeaderTheme" />
<LayoutTrigger
v-if=
"getShowContent && getShowHeaderTrigger"
:theme=
"getHeaderTheme"
:sider=
"false"
/>
<LayoutBreadcrumb
v-if=
"getShowContent && getShowBread && !getIsMobile"
:theme=
"getHeaderTheme"
/>
</div>
<!-- left end -->
<!-- menu start -->
<div
:class=
"`$
{prefixCls}-menu`" v-if="getShowTopMenu
&&
!getIsMobile">
<LayoutMenu
:isHorizontal=
"true"
:theme=
"getHeaderTheme"
:splitType=
"getSplitType"
:menuMode=
"getMenuMode"
/>
</div>
<!-- menu-end -->
<!-- action -->
<div
:class=
"`$
{prefixCls}-action`">
<AppSearch
v-if=
"!getIsMobile"
:class=
"`$
{prefixCls}-action__item`" />
<ErrorAction
v-if=
"getUseErrorHandle && !getIsMobile"
:class=
"`$
{prefixCls}-action__item`" />
<LockItem
v-if=
"getUseLockPage && !getIsMobile"
:class=
"`$
{prefixCls}-action__item`" />
<Notify
v-if=
"getShowNotice && !getIsMobile"
:class=
"`$
{prefixCls}-action__item`" />
<FullScreen
v-if=
"getShowFullScreen && !getIsMobile"
:class=
"`$
{prefixCls}-action__item`" />
<UserDropDown
:theme=
"getHeaderTheme"
/>
<AppLocalePicker
v-if=
"getShowLocale"
:reload=
"true"
:showText=
"false"
:class=
"`$
{prefixCls}-action__item`"
/>
</div>
</Header>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
unref
,
computed
}
from
'
vue
'
;
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
import
{
Layout
}
from
'
ant-design-vue
'
;
import
{
AppLogo
}
from
'
/@/components/Application
'
;
import
LayoutMenu
from
'
../menu
'
;
import
LayoutTrigger
from
'
../trigger/index.vue
'
;
import
{
AppSearch
}
from
'
/@/components/Application
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
useRootSetting
}
from
'
/@/hooks/setting/useRootSetting
'
;
import
{
useLocaleSetting
}
from
'
/@/hooks/setting/useLocaleSetting
'
;
import
{
MenuModeEnum
,
MenuSplitTyeEnum
}
from
'
/@/enums/menuEnum
'
;
import
{
AppLocalePicker
}
from
'
/@/components/Application
'
;
import
{
UserDropDown
,
LayoutBreadcrumb
,
FullScreen
,
Notify
,
LockItem
,
ErrorAction
,
}
from
'
./components
'
;
import
{
useAppInject
}
from
'
/@/hooks/web/useAppInject
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
export
default
defineComponent
({
name
:
'
LayoutHeader
'
,
components
:
{
Header
:
Layout
.
Header
,
AppLogo
,
LayoutTrigger
,
LayoutBreadcrumb
,
LayoutMenu
,
UserDropDown
,
AppLocalePicker
,
FullScreen
,
Notify
,
LockItem
,
AppSearch
,
ErrorAction
,
},
props
:
{
fixed
:
propTypes
.
bool
,
},
setup
(
props
)
{
const
{
prefixCls
}
=
useDesign
(
'
layout-header
'
);
const
{
getShowTopMenu
,
getShowHeaderTrigger
,
getSplit
}
=
useMenuSetting
();
const
{
getShowLocale
}
=
useLocaleSetting
();
const
{
getUseErrorHandle
}
=
useRootSetting
();
const
{
getHeaderTheme
,
getUseLockPage
,
getShowFullScreen
,
getShowNotice
,
getShowContent
,
getShowBread
,
getShowHeaderLogo
,
}
=
useHeaderSetting
();
const
{
getIsMobile
}
=
useAppInject
();
const
getHeaderClass
=
computed
(()
=>
{
const
theme
=
unref
(
getHeaderTheme
);
return
[
prefixCls
,
{
[
`
${
prefixCls
}
--fixed`
]:
props
.
fixed
,
[
`
${
prefixCls
}
--
${
theme
}
`
]:
theme
},
];
});
const
getSplitType
=
computed
(()
=>
{
return
unref
(
getSplit
)
?
MenuSplitTyeEnum
.
TOP
:
MenuSplitTyeEnum
.
NONE
;
});
const
getMenuMode
=
computed
(()
=>
{
return
unref
(
getSplit
)
?
MenuModeEnum
.
HORIZONTAL
:
null
;
});
return
{
prefixCls
,
getHeaderClass
,
getShowHeaderLogo
,
getHeaderTheme
,
getShowHeaderTrigger
,
getIsMobile
,
getShowBread
,
getShowContent
,
getSplitType
,
getMenuMode
,
getShowTopMenu
,
getShowLocale
,
getShowFullScreen
,
getShowNotice
,
getUseLockPage
,
getUseErrorHandle
,
};
},
});
</
script
>
<
style
lang=
"less"
>
@import './index.less';
</
style
>
src/layouts/default/index.vue
浏览文件 @
a65ad9ed
...
...
@@ -18,7 +18,7 @@
import
{
Layout
}
from
'
ant-design-vue
'
;
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
import
LayoutHeader
from
'
./header/
LayoutHeader
'
;
import
LayoutHeader
from
'
./header/
index.vue
'
;
import
LayoutContent
from
'
./content/index.vue
'
;
import
LayoutSideBar
from
'
./sider
'
;
import
LayoutMultipleHeader
from
'
./header/LayoutMultipleHeader
'
;
...
...
@@ -29,8 +29,6 @@
import
{
createLayoutContext
}
from
'
./useLayoutContext
'
;
import
{
registerGlobComp
}
from
'
/@/components/registerGlobComp
'
;
import
{
createBreakpointListen
}
from
'
/@/hooks/event/useBreakpoint
'
;
import
{
isMobile
}
from
'
/@/utils/is
'
;
export
default
defineComponent
({
name
:
'
DefaultLayout
'
,
...
...
@@ -44,22 +42,17 @@
Layout
,
},
setup
()
{
const
headerRef
=
ref
<
ComponentRef
>
(
null
);
const
isMobileRef
=
ref
(
false
);
const
{
prefixCls
}
=
useDesign
(
'
default-layout
'
);
createLayoutContext
({
fullHeader
:
headerRef
,
isMobile
:
isMobileRef
});
createBreakpointListen
(()
=>
{
isMobileRef
.
value
=
isMobile
();
});
// ! Only register global components here
// ! Can reduce the size of the first screen code
// default layout It is loaded after login. So it won’t be packaged to the first screen
registerGlobComp
();
const
headerRef
=
ref
<
ComponentRef
>
(
null
);
const
{
prefixCls
}
=
useDesign
(
'
default-layout
'
);
createLayoutContext
({
fullHeader
:
headerRef
});
const
{
getShowFullHeaderRef
}
=
useHeaderSetting
();
const
{
getShowSidebar
}
=
useMenuSetting
();
...
...
src/layouts/default/menu/index.tsx
浏览文件 @
a65ad9ed
...
...
@@ -9,6 +9,7 @@ import { AppLogo } from '/@/components/Application';
import
{
MenuModeEnum
,
MenuSplitTyeEnum
}
from
'
/@/enums/menuEnum
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
ScrollContainer
}
from
'
/@/components/Container
'
;
import
{
useGo
}
from
'
/@/hooks/web/usePage
'
;
import
{
useSplitMenu
}
from
'
./useLayoutMenu
'
;
...
...
@@ -16,6 +17,7 @@ import { openWindow } from '/@/utils';
import
{
propTypes
}
from
'
/@/utils/propTypes
'
;
import
{
isUrl
}
from
'
/@/utils/is
'
;
import
{
useRootSetting
}
from
'
/@/hooks/setting/useRootSetting
'
;
import
{
CSSProperties
}
from
'
vue
'
;
export
default
defineComponent
({
name
:
'
LayoutMenu
'
,
...
...
@@ -53,12 +55,25 @@ export default defineComponent({
const
getComputedMenuMode
=
computed
(()
=>
props
.
menuMode
||
unref
(
getMenuMode
));
const
getComputedMenuTheme
=
computed
(()
=>
props
.
theme
||
unref
(
getMenuTheme
));
const
showLogo
=
computed
(()
=>
unref
(
getShowLogo
)
&&
unref
(
getIsSidebarType
));
const
appendClass
=
computed
(()
=>
props
.
splitType
===
MenuSplitTyeEnum
.
TOP
);
const
getIsShowLogo
=
computed
(()
=>
unref
(
getShowLogo
)
&&
unref
(
getIsSidebarType
));
const
getUseScroll
=
computed
(()
=>
{
return
unref
(
getIsSidebarType
)
||
props
.
splitType
===
MenuSplitTyeEnum
.
LEFT
;
});
const
getWrapperStyle
=
computed
(
():
CSSProperties
=>
{
return
{
height
:
`calc(100% -
${
unref
(
getIsShowLogo
)
?
'
48px
'
:
'
0px
'
}
)`
,
};
}
);
/**
* click menu
* @param menu
*/
function
handleMenuClick
(
path
:
string
)
{
go
(
path
);
}
...
...
@@ -76,7 +91,7 @@ export default defineComponent({
}
function
renderHeader
()
{
if
(
!
unref
(
s
howLogo
))
return
null
;
if
(
!
unref
(
getIsS
howLogo
))
return
null
;
return
(
<
AppLogo
...
...
@@ -87,7 +102,7 @@ export default defineComponent({
);
}
return
()
=>
{
function
renderMenu
()
{
return
(
<
BasicMenu
beforeClickFn
=
{
beforeMenuClickFn
}
...
...
@@ -99,13 +114,22 @@ export default defineComponent({
items
=
{
unref
(
menusRef
)
}
accordion
=
{
unref
(
getAccordion
)
}
onMenuClick
=
{
handleMenuClick
}
appendClass
=
{
unref
(
appendClass
)
}
showLogo
=
{
unref
(
showLogo
)
}
>
{
{
header
:
()
=>
renderHeader
(),
}
}
</
BasicMenu
>
showLogo
=
{
unref
(
getIsShowLogo
)
}
/>
);
}
return
()
=>
{
return
(
<>
{
renderHeader
()
}
{
unref
(
getUseScroll
)
?
(
<
ScrollContainer
style
=
{
unref
(
getWrapperStyle
)
}
>
{
()
=>
renderMenu
()
}
</
ScrollContainer
>
)
:
(
renderMenu
()
)
}
;
</>
);
};
},
...
...
src/layouts/default/menu/useLayoutMenu.ts
浏览文件 @
a65ad9ed
...
...
@@ -14,9 +14,7 @@ import { permissionStore } from '/@/store/modules/permission';
export
function
useSplitMenu
(
splitType
:
Ref
<
MenuSplitTyeEnum
>
)
{
// Menu array
const
menusRef
=
ref
<
Menu
[]
>
([]);
const
{
currentRoute
}
=
useRouter
();
const
{
setMenuSetting
,
getIsHorizontal
,
getSplit
}
=
useMenuSetting
();
const
[
throttleHandleSplitLeftMenu
]
=
useThrottle
(
handleSplitLeftMenu
,
50
);
...
...
@@ -25,9 +23,11 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
()
=>
unref
(
splitType
)
!==
MenuSplitTyeEnum
.
LEFT
&&
!
unref
(
getIsHorizontal
)
);
const
splitLeft
=
computed
(()
=>
!
unref
(
getSplit
)
||
unref
(
splitType
)
!==
MenuSplitTyeEnum
.
LEFT
);
const
getSplitLeft
=
computed
(
()
=>
!
unref
(
getSplit
)
||
unref
(
splitType
)
!==
MenuSplitTyeEnum
.
LEFT
);
const
s
piltTop
=
computed
(()
=>
unref
(
splitType
)
===
MenuSplitTyeEnum
.
TOP
);
const
getS
piltTop
=
computed
(()
=>
unref
(
splitType
)
===
MenuSplitTyeEnum
.
TOP
);
const
normalType
=
computed
(()
=>
{
return
unref
(
splitType
)
===
MenuSplitTyeEnum
.
NONE
||
!
unref
(
getSplit
);
...
...
@@ -65,7 +65,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
// Handle left menu split
async
function
handleSplitLeftMenu
(
parentPath
:
string
)
{
if
(
unref
(
s
plitLeft
))
return
;
if
(
unref
(
getS
plitLeft
))
return
;
// spilt mode left
const
children
=
await
getChildrenMenus
(
parentPath
);
...
...
@@ -88,7 +88,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
}
// split-top
if
(
unref
(
s
piltTop
))
{
if
(
unref
(
getS
piltTop
))
{
const
shallowMenus
=
await
getShallowMenus
();
menusRef
.
value
=
shallowMenus
;
...
...
src/layouts/default/setting/SettingDrawer.tsx
浏览文件 @
a65ad9ed
...
...
@@ -80,7 +80,7 @@ export default defineComponent({
getShowSearch
,
}
=
useHeaderSetting
();
const
{
getShowMultipleTab
,
getShowQuick
}
=
useMultipleTabSetting
();
const
{
getShowMultipleTab
,
getShowQuick
,
getShowRedo
}
=
useMultipleTabSetting
();
const
getShowMenuRef
=
computed
(()
=>
{
return
unref
(
getShowMenu
)
&&
!
unref
(
getIsHorizontal
);
...
...
@@ -246,6 +246,13 @@ export default defineComponent({
def
=
{
unref
(
getShowMultipleTab
)
}
/>
<
SwitchItem
title
=
{
t
(
'
layout.setting.tabsRedoBtn
'
)
}
event
=
{
HandlerEnum
.
TABS_SHOW_REDO
}
def
=
{
unref
(
getShowRedo
)
}
disabled
=
{
!
unref
(
getShowMultipleTab
)
}
/>
<
SwitchItem
title
=
{
t
(
'
layout.setting.tabsQuickBtn
'
)
}
event
=
{
HandlerEnum
.
TABS_SHOW_QUICK
}
...
...
src/layouts/default/setting/enum.ts
浏览文件 @
a65ad9ed
...
...
@@ -31,6 +31,7 @@ export enum HandlerEnum {
HEADER_SEARCH
,
TABS_SHOW_QUICK
,
TABS_SHOW_REDO
,
TABS_SHOW
,
LOCK_TIME
,
...
...
src/layouts/default/setting/handler.ts
浏览文件 @
a65ad9ed
...
...
@@ -113,6 +113,8 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
case
HandlerEnum
.
TABS_SHOW
:
return
{
multiTabsSetting
:
{
show
:
value
}
};
case
HandlerEnum
.
TABS_SHOW_REDO
:
return
{
multiTabsSetting
:
{
showRedo
:
value
}
};
// ============header==================
case
HandlerEnum
.
HEADER_THEME
:
...
...
src/layouts/default/sider/index.less
浏览文件 @
a65ad9ed
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-sideBar';
.
layout-sidebar
{
// overflow: hidden
;
.
@{prefix-cls}
{
z-index: @layout-sider-fixed-z-index
;
&
.
fixed {
&
--
fixed {
position: fixed;
top: 0;
left: 0;
height: 100%;
}
&--mix {
top: @header-height;
height: calc(100% - @header-height);
}
&.ant-layout-sider-dark {
background: @sider-dark-bg-color;
...
...
src/layouts/default/sider/index.tsx
浏览文件 @
a65ad9ed
import
'
./index.less
'
;
import
{
computed
,
defineComponent
,
ref
,
unref
,
watch
,
nextTick
,
CSSProperties
}
from
'
vue
'
;
import
{
computed
,
defineComponent
,
ref
,
unref
,
CSSProperties
}
from
'
vue
'
;
import
{
Layout
}
from
'
ant-design-vue
'
;
import
LayoutMenu
from
'
../menu
'
;
...
...
@@ -8,14 +8,13 @@ import LayoutMenu from '../menu';
import
{
MenuModeEnum
,
MenuSplitTyeEnum
}
from
'
/@/enums/menuEnum
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
useTrigger
,
useDragLine
,
useSiderEvent
}
from
'
./useLayoutSider
'
;
import
{
useLayoutContext
}
from
'
../useLayoutContext
'
;
import
{
useAppInject
}
from
'
/@/hooks/web/useAppInject
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
export
default
defineComponent
({
name
:
'
LayoutSideBar
'
,
setup
()
{
const
topRef
=
ref
(
0
);
const
dragBarRef
=
ref
<
ElRef
>
(
null
);
const
sideRef
=
ref
<
ElRef
>
(
null
);
...
...
@@ -27,22 +26,18 @@ export default defineComponent({
getRealWidth
,
getMenuHidden
,
getMenuFixed
,
getIsMixMode
,
}
=
useMenuSetting
();
const
{
getShowFullHeaderRef
,
getUnFixedAndFull
}
=
useHeaderSetting
();
const
injectValue
=
useLayoutContext
();
const
{
prefixCls
}
=
useDesign
(
'
layout-sideBar
'
);
const
{
getTriggerAttr
,
getTriggerSlot
}
=
useTrigger
();
const
{
getIsMobile
}
=
useAppInject
();
const
{
renderDragLine
}
=
useDragLine
(
sideRef
,
dragBarRef
);
const
{
getCollapsedWidth
,
onBreakpointChange
,
onCollapseChange
,
onSiderClick
,
}
=
useSiderEvent
();
const
{
getCollapsedWidth
,
onBreakpointChange
,
onCollapseChange
}
=
useSiderEvent
();
const
getMode
=
computed
(()
=>
{
return
unref
(
getSplit
)
?
MenuModeEnum
.
INLINE
:
null
;
...
...
@@ -57,40 +52,16 @@ export default defineComponent({
});
const
getSiderClass
=
computed
(()
=>
{
return
{
'
layout-sidebar
'
:
true
,
fixed
:
unref
(
getMenuFixed
),
hidden
:
!
unref
(
showClassSideBarRef
),
};
return
[
prefixCls
,
{
[
`
${
prefixCls
}
--fixed`
]:
unref
(
getMenuFixed
),
hidden
:
!
unref
(
showClassSideBarRef
),
[
`
${
prefixCls
}
--mix`
]:
unref
(
getIsMixMode
),
},
];
});
const
getSiderStyle
=
computed
(()
=>
{
const
top
=
`
${
unref
(
topRef
)}
px`
;
if
(
!
unref
(
getMenuFixed
))
{
return
{
top
};
}
return
{
top
,
height
:
`calc(100% -
${
top
}
)`
,
};
});
watch
(
()
=>
getShowFullHeaderRef
.
value
,
()
=>
{
topRef
.
value
=
0
;
if
(
unref
(
getUnFixedAndFull
))
return
;
nextTick
(()
=>
{
const
fullHeaderEl
=
unref
(
injectValue
.
fullHeader
)?.
$el
;
if
(
!
fullHeaderEl
)
return
;
topRef
.
value
=
fullHeaderEl
.
offsetHeight
;
});
},
{
immediate
:
true
,
}
);
const
getHiddenDomStyle
=
computed
(
():
CSSProperties
=>
{
const
width
=
`
${
unref
(
getRealWidth
)}
px`
;
...
...
@@ -121,7 +92,7 @@ export default defineComponent({
return
()
=>
{
return
(
<>
{
unref
(
getMenuFixed
)
&&
!
unref
(
injectValue
.
i
sMobile
)
&&
(
{
unref
(
getMenuFixed
)
&&
!
unref
(
getI
sMobile
)
&&
(
<
div
style
=
{
unref
(
getHiddenDomStyle
)
}
class
=
{
{
hidden
:
!
unref
(
showClassSideBarRef
)
}
}
/>
)
}
<
Layout
.
Sider
...
...
@@ -129,12 +100,10 @@ export default defineComponent({
breakpoint
=
"lg"
collapsible
class
=
{
unref
(
getSiderClass
)
}
style
=
{
unref
(
getSiderStyle
)
}
width
=
{
unref
(
getMenuWidth
)
}
collapsed
=
{
unref
(
getCollapsed
)
}
collapsedWidth
=
{
unref
(
getCollapsedWidth
)
}
theme
=
{
unref
(
getMenuTheme
)
}
onClick
=
{
onSiderClick
}
onCollapse
=
{
onCollapseChange
}
onBreakpoint
=
{
onBreakpointChange
}
{
...
unref
(
getTriggerAttr
)
}
...
...
src/layouts/default/sider/useLayoutSider.tsx
浏览文件 @
a65ad9ed
...
...
@@ -16,7 +16,7 @@ export function useSiderEvent() {
const
brokenRef
=
ref
(
false
);
const
collapseRef
=
ref
(
true
);
const
{
setMenuSetting
,
getCollapsed
,
getMiniWidthNumber
,
getShowMenu
}
=
useMenuSetting
();
const
{
setMenuSetting
,
getCollapsed
,
getMiniWidthNumber
}
=
useMenuSetting
();
const
getCollapsedWidth
=
computed
(()
=>
{
return
unref
(
brokenRef
)
?
0
:
unref
(
getMiniWidthNumber
);
...
...
@@ -36,12 +36,7 @@ export function useSiderEvent() {
brokenRef
.
value
=
broken
;
}
function
onSiderClick
(
e
:
ChangeEvent
)
{
if
(
!
e
||
!
e
.
target
||
e
.
target
.
className
!==
'
basic-menu__content
'
)
return
;
if
(
!
unref
(
getCollapsed
)
||
!
unref
(
getShowMenu
))
return
;
setMenuSetting
({
collapsed
:
false
});
}
return
{
getCollapsedWidth
,
onCollapseChange
,
onBreakpointChange
,
onSiderClick
};
return
{
getCollapsedWidth
,
onCollapseChange
,
onBreakpointChange
};
}
/**
...
...
src/layouts/default/tabs/components/TabContent.vue
浏览文件 @
a65ad9ed
...
...
@@ -4,7 +4,7 @@
<span
class=
"ml-1"
>
{{
getTitle
}}
</span>
</div>
<span
:class=
"`$
{prefixCls}__extra
`" v-else
>
<span
:class=
"`$
{prefixCls}__extra
-quick`" v-else @click="handleContext"
>
<RightOutlined
/>
</span>
</Dropdown>
...
...
src/layouts/default/tabs/components/TabRedo.vue
0 → 100644
浏览文件 @
a65ad9ed
<
template
>
<Tooltip
:title=
"t('layout.multipleTab.tooltipRedo')"
placement=
"bottom"
:mouseEnterDelay=
"0.5"
>
<span
:class=
"`$
{prefixCls}__extra-redo`" @click="handleRedo">
<RedoOutlined
:spin=
"loading"
/>
</span>
</Tooltip>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
ref
}
from
'
vue
'
;
import
{
RedoOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
Tooltip
}
from
'
ant-design-vue
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useTabs
}
from
'
/@/hooks/web/useTabs
'
;
export
default
defineComponent
({
name
:
'
TabContent
'
,
components
:
{
RedoOutlined
,
Tooltip
},
setup
()
{
const
loading
=
ref
(
false
);
const
{
prefixCls
}
=
useDesign
(
'
multiple-tabs-content
'
);
const
{
t
}
=
useI18n
();
const
{
refreshPage
}
=
useTabs
();
async
function
handleRedo
()
{
loading
.
value
=
true
;
await
refreshPage
();
setTimeout
(()
=>
{
loading
.
value
=
false
;
// Animation execution time
},
1000
);
}
return
{
prefixCls
,
t
,
handleRedo
,
loading
};
},
});
</
script
>
src/layouts/default/tabs/index.less
浏览文件 @
a65ad9ed
...
...
@@ -34,30 +34,30 @@
border: 1px solid darken(@border-color-light, 6%);
transition: none;
&:not(.ant-tabs-tab-active)::after
{
position: absolute;
bottom
: -1px;
left: 50%;
width: 100%;
height: 2px;
background-color: @primary-color;
content: '';
opacity: 0;
transform: translate(-50%, 0) scaleX(0);
transform-origin: center;
transition: none;
}
// &:not(.ant-tabs-tab-active)::before
{
//
position: absolute;
// top
: -1px;
//
left: 50%;
//
width: 100%;
//
height: 2px;
//
background-color: @primary-color;
//
content: '';
//
opacity: 0;
//
transform: translate(-50%, 0) scaleX(0);
//
transform-origin: center;
//
transition: none;
//
}
&:hover {
.ant-tabs-close-x {
opacity: 1;
}
&:not(.ant-tabs-tab-active)::after
{
opacity: 1;
transform: translate(-50%, 0) scaleX(1);
transition: all 0.3s ease-in-out;
}
// &:not(.ant-tabs-tab-active)::before
{
//
opacity: 1;
//
transform: translate(-50%, 0) scaleX(1);
//
transition: all 0.3s ease-in-out;
//
}
}
.ant-tabs-close-x {
...
...
@@ -152,12 +152,13 @@
}
&-content {
&__extra {
&__extra-quick,
&__extra-redo {
display: inline-block;
width:
@multiple-height
;
width:
36px
;
height: @multiple-height;
line-height: @multiple-height;
color: #
999
;
color: #
666
;
text-align: center;
cursor: pointer;
border-left: 1px solid #eee;
...
...
@@ -171,6 +172,12 @@
}
}
&__extra-redo {
span[role='img'] {
transform: rotate(0deg);
}
}
&__info {
display: inline-block;
width: 100%;
...
...
src/layouts/default/tabs/index.vue
浏览文件 @
a65ad9ed
...
...
@@ -17,14 +17,16 @@
</
template
>
</TabPane>
</template>
<
template
#tabBarExtraContent
>
<QuickButton
/>
<
template
#tabBarExtraContent
v-if=
"getShowRedo || getShowQuick"
>
<TabRedo
v-if=
"getShowRedo"
/>
<QuickButton
v-if=
"getShowQuick"
/>
</
template
>
</Tabs>
</div>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
,
watch
,
computed
,
unref
,
ref
}
from
'
vue
'
;
import
{
defineComponent
,
computed
,
unref
,
ref
}
from
'
vue
'
;
import
{
Tabs
}
from
'
ant-design-vue
'
;
import
TabContent
from
'
./components/TabContent.vue
'
;
...
...
@@ -38,61 +40,52 @@
import
{
REDIRECT_NAME
}
from
'
/@/router/constant
'
;
import
{
useDesign
}
from
'
/@/hooks/web/useDesign
'
;
import
{
createAsyncComponent
}
from
'
/@/utils/factory/createAsyncComponent
'
;
import
{
listenerLastChangeTab
}
from
'
/@/logics/mitt/tabChange
'
;
import
{
useMultipleTabSetting
}
from
'
/@/hooks/setting/useMultipleTabSetting
'
;
export
default
defineComponent
({
name
:
'
MultipleTabs
'
,
components
:
{
QuickButton
:
createAsyncComponent
(()
=>
import
(
'
./components/QuickButton.vue
'
)),
TabRedo
:
createAsyncComponent
(()
=>
import
(
'
./components/TabRedo.vue
'
)),
Tabs
,
TabPane
:
Tabs
.
TabPane
,
TabContent
,
},
setup
()
{
const
affixTextList
=
initAffixTabs
();
const
activeKeyRef
=
ref
(
''
);
useTabsDrag
(
affixTextList
);
const
{
prefixCls
}
=
useDesign
(
'
multiple-tabs
'
);
const
go
=
useGo
();
const
{
getShowQuick
,
getShowRedo
}
=
useMultipleTabSetting
();
const
getTabsState
=
computed
(()
=>
tabStore
.
getTabsState
);
const
unClose
=
computed
(()
=>
{
return
getTabsState
.
value
.
length
===
1
;
});
const
unClose
=
computed
(()
=>
unref
(
getTabsState
).
length
===
1
);
const
getWrapClass
=
computed
(()
=>
{
return
[
prefixCls
,
{
[
`
${
prefixCls
}
--hide-close`
]:
un
Close
,
[
`
${
prefixCls
}
--hide-close`
]:
un
ref
(
unClose
)
,
},
];
});
watch
(
()
=>
tabStore
.
getLastChangeRouteState
?.
path
,
()
=>
{
if
(
tabStore
.
getLastChangeRouteState
?.
name
===
REDIRECT_NAME
)
{
return
;
}
const
lastChangeRoute
=
unref
(
tabStore
.
getLastChangeRouteState
);
if
(
!
lastChangeRoute
||
!
userStore
.
getTokenState
)
return
;
const
{
path
,
fullPath
}
=
lastChangeRoute
;
const
p
=
fullPath
||
path
;
if
(
activeKeyRef
.
value
!==
p
)
{
activeKeyRef
.
value
=
p
;
}
tabStore
.
addTabAction
(
lastChangeRoute
);
},
{
immediate
:
true
,
listenerLastChangeTab
((
route
)
=>
{
const
{
name
}
=
route
;
if
(
name
===
REDIRECT_NAME
||
!
route
||
!
userStore
.
getTokenState
)
return
;
const
{
path
,
fullPath
}
=
route
;
const
p
=
fullPath
||
path
;
if
(
activeKeyRef
.
value
!==
p
)
{
activeKeyRef
.
value
=
p
;
}
);
tabStore
.
addTabAction
(
unref
(
route
));
});
function
handleChange
(
activeKey
:
any
)
{
activeKeyRef
.
value
=
activeKey
;
...
...
@@ -114,6 +107,8 @@
handleChange
,
activeKeyRef
,
getTabsState
,
getShowQuick
,
getShowRedo
,
};
},
});
...
...
src/layouts/default/tabs/useTabDropdown.ts
浏览文件 @
a65ad9ed
...
...
@@ -10,7 +10,6 @@ import { useTabs } from '/@/hooks/web/useTabs';
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
useHeaderSetting
}
from
'
/@/hooks/setting/useHeaderSetting
'
;
import
{
useMenuSetting
}
from
'
/@/hooks/setting/useMenuSetting
'
;
import
{
useMultipleTabSetting
}
from
'
/@/hooks/setting/useMultipleTabSetting
'
;
const
{
t
}
=
useI18n
();
...
...
@@ -24,11 +23,8 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
const
{
getShowMenu
,
setMenuSetting
}
=
useMenuSetting
();
const
{
getShowHeader
,
setHeaderSetting
}
=
useHeaderSetting
();
const
{
getShowQuick
}
=
useMultipleTabSetting
();
const
isTabs
=
computed
(()
=>
!
unref
(
getShowQuick
)
?
true
:
tabContentProps
.
type
===
TabContentEnum
.
TAB_TYPE
);
const
isTabs
=
computed
(()
=>
tabContentProps
.
type
===
TabContentEnum
.
TAB_TYPE
);
const
getCurrentTab
=
computed
(
():
RouteLocationNormalized
=>
{
...
...
src/layouts/default/useLayoutContext.ts
浏览文件 @
a65ad9ed
...
...
@@ -3,7 +3,6 @@ import { createContext, useContext } from '/@/hooks/core/useContext';
export
interface
LayoutContextProps
{
fullHeader
:
Ref
<
ComponentRef
>
;
isMobile
:
Ref
<
boolean
>
;
}
const
key
:
InjectionKey
<
LayoutContextProps
>
=
Symbol
();
...
...
src/locales/lang/en/layout/header.ts
浏览文件 @
a65ad9ed
...
...
@@ -7,7 +7,7 @@ export default {
tooltipErrorLog
:
'
Error log
'
,
tooltipLock
:
'
Lock screen
'
,
tooltipNotify
:
'
Notification
'
,
tooltipRedo
:
'
Refresh
'
,
tooltipEntryFull
:
'
Full Screen
'
,
tooltipExitFull
:
'
Exit Full Screen
'
,
...
...
src/locales/lang/en/layout/multipleTab.ts
浏览文件 @
a65ad9ed
...
...
@@ -7,4 +7,5 @@ export default {
closeAll
:
'
Close All
'
,
putAway
:
'
PutAway
'
,
unfold
:
'
Unfold
'
,
tooltipRedo
:
'
Refresh
'
,
};
src/locales/lang/en/layout/setting.ts
浏览文件 @
a65ad9ed
...
...
@@ -53,6 +53,7 @@ export default {
breadcrumbIcon
:
'
Breadcrumbs Icon
'
,
tabs
:
'
Tabs
'
,
tabsQuickBtn
:
'
Tabs quick button
'
,
tabsRedoBtn
:
'
Tabs redo button
'
,
sidebar
:
'
Sidebar
'
,
header
:
'
Header
'
,
footer
:
'
Footer
'
,
...
...
src/locales/lang/zh_CN/layout/header.ts
浏览文件 @
a65ad9ed
...
...
@@ -8,7 +8,7 @@ export default {
tooltipErrorLog
:
'
错误日志
'
,
tooltipLock
:
'
锁定屏幕
'
,
tooltipNotify
:
'
消息通知
'
,
tooltipRedo
:
'
刷新
'
,
tooltipEntryFull
:
'
全屏
'
,
tooltipExitFull
:
'
退出全屏
'
,
...
...
src/locales/lang/zh_CN/layout/multipleTab.ts
浏览文件 @
a65ad9ed
...
...
@@ -7,4 +7,5 @@ export default {
closeAll
:
'
关闭全部
'
,
putAway
:
'
收起
'
,
unfold
:
'
展开
'
,
tooltipRedo
:
'
刷新
'
,
};
src/locales/lang/zh_CN/layout/setting.ts
浏览文件 @
a65ad9ed
...
...
@@ -52,6 +52,7 @@ export default {
breadcrumbIcon
:
'
面包屑图标
'
,
tabs
:
'
标签页
'
,
tabsQuickBtn
:
'
标签页快捷按钮
'
,
tabsRedoBtn
:
'
标签页刷新按钮
'
,
sidebar
:
'
左侧菜单
'
,
header
:
'
顶栏
'
,
footer
:
'
页脚
'
,
...
...
src/logics/mitt/tabChange.ts
0 → 100644
浏览文件 @
a65ad9ed
/**
* Used to monitor routing changes to change the status of menus and tabs. There is no need to monitor the route, because the route status change is affected by the page rendering time, which will be slow
*/
import
Mitt
from
'
/@/utils/mitt
'
;
import
type
{
RouteLocationNormalized
}
from
'
vue-router
'
;
import
{
getRoute
}
from
'
/@/router/helper/routeHelper
'
;
const
mitt
=
new
Mitt
();
const
key
=
Symbol
();
let
lastChangeTab
:
RouteLocationNormalized
;
export
function
setLastChangeTab
(
lastChangeRoute
:
RouteLocationNormalized
)
{
mitt
.
emit
(
key
,
getRoute
(
lastChangeRoute
));
lastChangeTab
=
getRoute
(
lastChangeRoute
);
}
export
function
listenerLastChangeTab
(
callback
:
(
route
:
RouteLocationNormalized
)
=>
void
,
immediate
=
true
)
{
mitt
.
on
(
key
,
callback
);
if
(
immediate
)
{
callback
(
lastChangeTab
);
}
}
src/router/guard/index.ts
浏览文件 @
a65ad9ed
...
...
@@ -8,13 +8,12 @@ import { createPageLoadingGuard } from './pageLoadingGuard';
import
{
useGlobSetting
,
useProjectSetting
}
from
'
/@/hooks/setting
'
;
import
{
getRoute
}
from
'
/@/router/helper/routeHelper
'
;
import
{
setTitle
}
from
'
/@/utils/browser
'
;
import
{
AxiosCanceler
}
from
'
/@/utils/http/axios/axiosCancel
'
;
import
{
tabStore
}
from
'
/@/store/modules/tab
'
;
import
{
useI18n
}
from
'
/@/hooks/web/useI18n
'
;
import
{
REDIRECT_NAME
}
from
'
/@/router/constant
'
;
import
{
setLastChangeTab
}
from
'
/@/logics/mitt/tabChange
'
;
const
{
closeMessageOnSwitch
,
removeAllHttpPending
}
=
useProjectSetting
();
const
globSetting
=
useGlobSetting
();
...
...
@@ -35,8 +34,7 @@ export function createGuard(router: Router) {
router
.
beforeEach
(
async
(
to
)
=>
{
to
.
meta
.
loaded
=
!!
loadedPageMap
.
get
(
to
.
path
);
// Notify routing changes
tabStore
.
commitLastChangeRouteState
(
getRoute
(
to
));
setLastChangeTab
(
to
);
try
{
if
(
closeMessageOnSwitch
)
{
Modal
.
destroyAll
();
...
...
src/router/menus/index.ts
浏览文件 @
a65ad9ed
...
...
@@ -68,6 +68,7 @@ export async function getCurrentParentPath(currentPath: string) {
export
async
function
getShallowMenus
():
Promise
<
Menu
[]
>
{
const
menus
=
await
getAsyncMenus
();
const
routes
=
router
.
getRoutes
();
const
shallowMenuList
=
menus
.
map
((
item
)
=>
({
...
item
,
children
:
undefined
}));
return
!
isBackMode
()
?
shallowMenuList
.
filter
(
basicFilter
(
routes
))
:
shallowMenuList
;
}
...
...
src/settings/projectSetting.ts
浏览文件 @
a65ad9ed
...
...
@@ -62,8 +62,7 @@ const setting: ProjectConfig = {
theme
:
ThemeEnum
.
LIGHT
,
// Whether to enable the lock screen function
useLockPage
:
true
,
// Whether to show the refresh button
showRedo
:
true
,
// Whether to show the full screen button
showFullScreen
:
true
,
// Whether to show the document button
...
...
@@ -117,6 +116,9 @@ const setting: ProjectConfig = {
canDrag
:
true
,
// Turn on quick actions
showQuick
:
true
,
// Whether to show the refresh button
showRedo
:
true
,
},
// Transition Setting
...
...
src/store/modules/tab.ts
浏览文件 @
a65ad9ed
...
...
@@ -5,7 +5,6 @@ import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-dec
import
{
hotModuleUnregisterModule
}
from
'
/@/utils/helper/vuexHelper
'
;
import
{
PageEnum
}
from
'
/@/enums/pageEnum
'
;
import
{
userStore
}
from
'
./user
'
;
import
store
from
'
/@/store
'
;
import
router
from
'
/@/router
'
;
...
...
@@ -13,8 +12,7 @@ import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/constant';
import
{
RouteLocationNormalized
,
RouteLocationRaw
}
from
'
vue-router
'
;
import
{
getRoute
}
from
'
/@/router/helper/routeHelper
'
;
import
{
useGo
,
useRedo
}
from
'
/@/hooks/web/usePage
'
;
// declare namespace TabsStore {
import
{
cloneDeep
}
from
'
lodash-es
'
;
const
NAME
=
'
tab
'
;
...
...
@@ -34,19 +32,12 @@ class Tab extends VuexModule {
// tab list
tabsState
:
RouteLocationNormalized
[]
=
[];
// Last route change
lastChangeRouteState
:
RouteLocationNormalized
|
null
=
null
;
lastDragEndIndexState
=
0
;
get
getTabsState
()
{
return
this
.
tabsState
;
}
get
getLastChangeRouteState
()
{
return
this
.
lastChangeRouteState
;
}
get
getCurrentTab
():
RouteLocationNormalized
{
const
route
=
unref
(
router
.
currentRoute
);
return
this
.
tabsState
.
find
((
item
)
=>
item
.
path
===
route
.
path
)
!
;
...
...
@@ -60,12 +51,6 @@ class Tab extends VuexModule {
return
this
.
lastDragEndIndexState
;
}
@
Mutation
commitLastChangeRouteState
(
route
:
RouteLocationNormalized
):
void
{
if
(
!
userStore
.
getTokenState
)
return
;
this
.
lastChangeRouteState
=
route
;
}
@
Mutation
commitClearCache
():
void
{
this
.
cachedMapState
=
new
Map
();
...
...
@@ -152,7 +137,7 @@ class Tab extends VuexModule {
this
.
tabsState
.
splice
(
updateIndex
,
1
,
curTab
);
return
;
}
this
.
tabsState
.
push
(
route
);
this
.
tabsState
=
cloneDeep
([...
this
.
tabsState
,
route
]
);
}
/**
...
...
@@ -210,7 +195,7 @@ class Tab extends VuexModule {
}
@
Mutation
commitRedoPage
()
{
async
commitRedoPage
()
{
const
route
=
router
.
currentRoute
.
value
;
for
(
const
[
key
,
value
]
of
this
.
cachedMapState
)
{
const
index
=
value
.
findIndex
((
item
)
=>
item
===
(
route
.
name
as
string
));
...
...
@@ -225,7 +210,7 @@ class Tab extends VuexModule {
this
.
cachedMapState
.
set
(
key
,
value
);
}
const
redo
=
useRedo
();
redo
();
await
redo
();
}
@
Action
...
...
src/types/config.d.ts
浏览文件 @
a65ad9ed
...
...
@@ -27,6 +27,9 @@ export interface MultiTabsSetting {
// 开启快速操作
showQuick
:
boolean
;
canDrag
:
boolean
;
// 显示刷新按钮
showRedo
:
boolean
;
}
export
interface
HeaderSetting
{
...
...
@@ -34,8 +37,7 @@ export interface HeaderSetting {
fixed
:
boolean
;
show
:
boolean
;
theme
:
ThemeEnum
;
// 显示刷新按钮
showRedo
:
boolean
;
// 显示全屏按钮
showFullScreen
:
boolean
;
// 开启全屏功能
...
...
src/utils/mitt.ts
浏览文件 @
a65ad9ed
...
...
@@ -6,13 +6,13 @@
* @returns {Function} The function's instance
*/
export
default
class
Mitt
{
private
cache
:
Map
<
string
,
Array
<
(
data
:
any
)
=>
void
>>
;
private
cache
:
Map
<
string
|
Symbol
,
Array
<
(...
data
:
any
)
=>
void
>>
;
constructor
(
all
=
[])
{
// A Map of event names to registered handler functions.
this
.
cache
=
new
Map
(
all
);
}
once
(
type
:
string
,
handler
:
Fn
)
{
once
(
type
:
string
|
Symbol
,
handler
:
Fn
)
{
const
decor
=
(...
args
:
any
[])
=>
{
handler
&&
handler
.
apply
(
this
,
args
);
this
.
off
(
type
,
decor
);
...
...
@@ -27,7 +27,7 @@ export default class Mitt {
* @param {string|symbol} type Type of event to listen for, or `"*"` for all events
* @param {Function} handler Function to call in response to given event
*/
on
(
type
:
string
,
handler
:
Fn
)
{
on
(
type
:
string
|
Symbol
,
handler
:
Fn
)
{
const
handlers
=
this
.
cache
.
get
(
type
);
const
added
=
handlers
&&
handlers
.
push
(
handler
);
if
(
!
added
)
{
...
...
@@ -41,7 +41,7 @@ export default class Mitt {
* @param {string|symbol} type Type of event to unregister `handler` from, or `"*"`
* @param {Function} handler Handler function to remove
*/
off
(
type
:
string
,
handler
:
Fn
)
{
off
(
type
:
string
|
Symbol
,
handler
:
Fn
)
{
const
handlers
=
this
.
cache
.
get
(
type
);
if
(
handlers
)
{
handlers
.
splice
(
handlers
.
indexOf
(
handler
)
>>>
0
,
1
);
...
...
@@ -57,7 +57,7 @@ export default class Mitt {
* @param {string|symbol} type The event type to invoke
* @param {*} [evt] Any value (object is recommended and powerful), passed to each handler
*/
emit
(
type
:
string
,
evt
:
any
)
{
emit
(
type
:
string
|
Symbol
,
evt
:
any
)
{
for
(
const
handler
of
(
this
.
cache
.
get
(
type
)
||
[]).
slice
())
handler
(
evt
);
for
(
const
handler
of
(
this
.
cache
.
get
(
'
*
'
)
||
[]).
slice
())
handler
(
type
,
evt
);
}
...
...
src/views/sys/lock/LockPage.vue
浏览文件 @
a65ad9ed
...
...
@@ -144,7 +144,7 @@
right: 0;
bottom: 0;
left: 0;
z-index:
3000
;
z-index:
@lock-page-z-index
;
display: flex;
width: 100vw;
height: 100vh;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录