feat(Layout): Add cutMenu layout
parent
1522e925ba
commit
ff4dd3afbf
@ -0,0 +1,3 @@
|
|||||||
|
import TabMenu from './src/TabMenu.vue'
|
||||||
|
|
||||||
|
export { TabMenu }
|
||||||
@ -0,0 +1,211 @@
|
|||||||
|
<script lang="tsx">
|
||||||
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
|
import { useAppStore } from '@/store/modules/app'
|
||||||
|
import { computed, unref, defineComponent, watch, ref } from 'vue'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
import { ElScrollbar } from 'element-plus'
|
||||||
|
import { Icon } from '@/components/Icon'
|
||||||
|
import { Menu } from '@/components/Menu'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { pathResolve } from '@/utils/routerHelper'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { filterMenusPath, initTabMap, tabPathMap } from './helper'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TabMenu',
|
||||||
|
setup() {
|
||||||
|
const { push, currentRoute } = useRouter()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const collapse = computed(() => appStore.getCollapse)
|
||||||
|
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
const routers = computed(() => permissionStore.getRouters)
|
||||||
|
|
||||||
|
const tabRouters = computed(() => unref(routers).filter((v) => !v?.meta?.hidden))
|
||||||
|
|
||||||
|
const setCollapse = () => {
|
||||||
|
appStore.setCollapse(!unref(collapse))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => routers.value,
|
||||||
|
(routers: AppRouteRecordRaw[]) => {
|
||||||
|
initTabMap(routers)
|
||||||
|
filterMenusPath(routers, routers)
|
||||||
|
console.log(tabPathMap)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const showTitle = ref(true)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => collapse.value,
|
||||||
|
(collapse: boolean) => {
|
||||||
|
if (!collapse) {
|
||||||
|
setTimeout(() => {
|
||||||
|
showTitle.value = !collapse
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
showTitle.value = !collapse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 是否显示菜单
|
||||||
|
const showMenu = ref(false)
|
||||||
|
|
||||||
|
// tab高亮
|
||||||
|
const tabActive = ref('')
|
||||||
|
|
||||||
|
// tab点击事件
|
||||||
|
const tabClick = (item: AppRouteRecordRaw) => {
|
||||||
|
tabActive.value = item.children ? item.path : item.path.split('/')[0]
|
||||||
|
if (item.children) {
|
||||||
|
showMenu.value = !unref(showMenu)
|
||||||
|
if (unref(showMenu)) {
|
||||||
|
permissionStore.setMenuTabRouters(
|
||||||
|
cloneDeep(item.children).map((v) => {
|
||||||
|
v.path = pathResolve(unref(tabActive), v.path)
|
||||||
|
return v
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push(item.path)
|
||||||
|
permissionStore.setMenuTabRouters([])
|
||||||
|
showMenu.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置高亮
|
||||||
|
const isActice = (currentPath: string) => {
|
||||||
|
const { path } = unref(currentRoute)
|
||||||
|
if (tabPathMap[currentPath].includes(path)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const mouseleave = () => {
|
||||||
|
if (!unref(showMenu)) return
|
||||||
|
showMenu.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
'v-tab-menu relative bg-[var(--left-menu-bg-color)] top-1px',
|
||||||
|
{
|
||||||
|
'w-[var(--tab-menu-max-width)]': !unref(collapse),
|
||||||
|
'w-[var(--tab-menu-min-width)]': unref(collapse)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onMouseleave={mouseleave}
|
||||||
|
>
|
||||||
|
<ElScrollbar class="!h-[calc(100%-var(--tab-menu-collapse-height)-1px)]">
|
||||||
|
<div>
|
||||||
|
{() => {
|
||||||
|
return unref(tabRouters).map((v) => {
|
||||||
|
const item = (
|
||||||
|
v?.children?.length && v?.children?.length > 1
|
||||||
|
? v
|
||||||
|
: {
|
||||||
|
...(v?.children && v?.children[0]),
|
||||||
|
path: pathResolve(v.path, (v?.children && v?.children[0])?.path as string)
|
||||||
|
}
|
||||||
|
) as AppRouteRecordRaw
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
'v-tab-menu-item text-center text-12px relative py-12px cursor-pointer',
|
||||||
|
{
|
||||||
|
'is-active': isActice(v.path)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onClick={() => {
|
||||||
|
tabClick(item)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Icon icon={item?.meta?.icon}></Icon>
|
||||||
|
</div>
|
||||||
|
{!unref(showTitle) ? undefined : (
|
||||||
|
<p class="break-words mt-5px px-2px">{t(item.meta?.title)}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</ElScrollbar>
|
||||||
|
<div
|
||||||
|
class="v-tab-menu-collapse text-center h-[var(--tab-menu-collapse-height)] leading-[var(--tab-menu-collapse-height)] cursor-pointer"
|
||||||
|
onClick={setCollapse}
|
||||||
|
>
|
||||||
|
<Icon icon={unref(collapse) ? 'ep:d-arrow-right' : 'ep:d-arrow-left'}></Icon>
|
||||||
|
</div>
|
||||||
|
<Menu
|
||||||
|
class={[
|
||||||
|
'!absolute top-0 border-left-1 border-solid border-[var(--left-menu-bg-light-color)]',
|
||||||
|
{
|
||||||
|
'!left-[var(--tab-menu-min-width)]': unref(collapse),
|
||||||
|
'!left-[var(--tab-menu-max-width)]': !unref(collapse),
|
||||||
|
'!w-[calc(var(--left-menu-max-width)+1px)]': unref(showMenu),
|
||||||
|
'!w-0': !unref(showMenu)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
style="transition: width var(--transition-time-02), left var(--transition-time-02);"
|
||||||
|
></Menu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@prefix-cls: ~'@{namespace}-tab-menu';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
transition: all var(--transition-time-02);
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid var(--left-menu-border-color);
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
color: var(--left-menu-text-color);
|
||||||
|
transition: all var(--transition-time-02);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--left-menu-text-active-color);
|
||||||
|
// background-color: var(--left-menu-bg-active-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-collapse {
|
||||||
|
color: var(--left-menu-text-color);
|
||||||
|
background-color: var(--left-menu-bg-light-color);
|
||||||
|
border-top: 1px solid var(--left-menu-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active {
|
||||||
|
color: var(--left-menu-text-active-color);
|
||||||
|
background-color: var(--left-menu-bg-active-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import { getAllParentPath } from '@/components/Menu/src/helper'
|
||||||
|
import type { RouteMeta } from 'vue-router'
|
||||||
|
import { isUrl } from '@/utils/is'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
export type TabMapTypes = {
|
||||||
|
[key: string]: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tabPathMap = reactive<TabMapTypes>({})
|
||||||
|
|
||||||
|
export const initTabMap = (routes: AppRouteRecordRaw[]) => {
|
||||||
|
for (const v of routes) {
|
||||||
|
const meta = (v.meta ?? {}) as RouteMeta
|
||||||
|
if (!meta?.hidden) {
|
||||||
|
tabPathMap[v.path] = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const filterMenusPath = (
|
||||||
|
routes: AppRouteRecordRaw[],
|
||||||
|
allRoutes: AppRouteRecordRaw[]
|
||||||
|
): AppRouteRecordRaw[] => {
|
||||||
|
const res: AppRouteRecordRaw[] = []
|
||||||
|
for (const v of routes) {
|
||||||
|
let data: Nullable<AppRouteRecordRaw> = null
|
||||||
|
const meta = (v.meta ?? {}) as RouteMeta
|
||||||
|
if (!meta.hidden) {
|
||||||
|
const allParentPaht = getAllParentPath<AppRouteRecordRaw>(allRoutes, v.path)
|
||||||
|
|
||||||
|
const fullPath = isUrl(v.path) ? v.path : allParentPaht.join('/')
|
||||||
|
|
||||||
|
data = cloneDeep(v)
|
||||||
|
data.path = fullPath
|
||||||
|
if (v.children && data) {
|
||||||
|
data.children = filterMenusPath(v.children, allRoutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
res.push(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allParentPaht.length && Reflect.has(tabPathMap, allParentPaht[0])) {
|
||||||
|
tabPathMap[allParentPaht[0]].push(fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
@import './var.css';
|
@import './var.css';
|
||||||
@import './common.less';
|
// @import './common.less';
|
||||||
|
|||||||
Loading…
Reference in New Issue