提交 | 用户 | age
|
2f6253
|
1 |
import type { MenuState } from './types'; |
陈 |
2 |
import type { Menu as MenuType } from '/@/router/types'; |
|
3 |
|
5f2a92
|
4 |
import { computed, defineComponent, unref, reactive, toRef, watch, onMounted, ref } from 'vue'; |
2f6253
|
5 |
import { Menu } from 'ant-design-vue'; |
陈 |
6 |
import SearchInput from './SearchInput.vue'; |
|
7 |
import MenuContent from './MenuContent'; |
96c10d
|
8 |
|
V |
9 |
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; |
|
10 |
|
|
11 |
import { menuStore } from '/@/store/modules/menu'; |
|
12 |
import { appStore } from '/@/store/modules/app'; |
|
13 |
|
2f6253
|
14 |
import { useSearchInput } from './useSearchInput'; |
陈 |
15 |
import { useOpenKeys } from './useOpenKeys'; |
|
16 |
import { useRouter } from 'vue-router'; |
96c10d
|
17 |
|
2f6253
|
18 |
import { isFunction } from '/@/utils/is'; |
96c10d
|
19 |
import { getSlot } from '/@/utils/helper/tsxHelper'; |
V |
20 |
import { menuHasChildren } from './helper'; |
|
21 |
|
2f6253
|
22 |
import { getCurrentParentPath } from '/@/router/menus'; |
96c10d
|
23 |
|
V |
24 |
import { basicProps } from './props'; |
|
25 |
import './index.less'; |
2f6253
|
26 |
export default defineComponent({ |
陈 |
27 |
name: 'BasicMenu', |
|
28 |
props: basicProps, |
|
29 |
emits: ['menuClick'], |
|
30 |
setup(props, { slots, emit }) { |
|
31 |
const currentParentPath = ref(''); |
|
32 |
const menuState = reactive<MenuState>({ |
|
33 |
defaultSelectedKeys: [], |
|
34 |
mode: props.mode, |
|
35 |
theme: computed(() => props.theme), |
|
36 |
openKeys: [], |
|
37 |
searchValue: '', |
|
38 |
selectedKeys: [], |
|
39 |
collapsedOpenKeys: [], |
|
40 |
}); |
|
41 |
const { currentRoute } = useRouter(); |
|
42 |
|
|
43 |
const { handleInputChange, handleInputClick } = useSearchInput({ |
|
44 |
flatMenusRef: toRef(props, 'flatItems'), |
|
45 |
emit: emit, |
|
46 |
menuState, |
|
47 |
handleMenuChange, |
|
48 |
}); |
|
49 |
|
|
50 |
const { handleOpenChange, resetKeys, setOpenKeys } = useOpenKeys( |
|
51 |
menuState, |
|
52 |
toRef(props, 'items'), |
|
53 |
toRef(props, 'flatItems'), |
2f1255
|
54 |
toRef(props, 'isAppMenu'), |
4ff6b7
|
55 |
toRef(props, 'mode'), |
V |
56 |
toRef(props, 'accordion') |
2f6253
|
57 |
); |
陈 |
58 |
|
|
59 |
const getOpenKeys = computed(() => { |
|
60 |
if (props.isAppMenu) { |
|
61 |
return menuStore.getCollapsedState ? menuState.collapsedOpenKeys : menuState.openKeys; |
|
62 |
} |
|
63 |
return menuState.openKeys; |
|
64 |
}); |
f64568
|
65 |
|
2f6253
|
66 |
// menu外层样式 |
陈 |
67 |
const getMenuWrapStyle = computed((): any => { |
|
68 |
const { showLogo, search } = props; |
|
69 |
let offset = 0; |
|
70 |
if (search) { |
|
71 |
offset += 54; |
|
72 |
} |
84b830
|
73 |
if (showLogo) { |
V |
74 |
offset += 46; |
|
75 |
} |
2f6253
|
76 |
return { |
4f6b65
|
77 |
height: `calc(100% - ${offset - 12}px)`, |
2f6253
|
78 |
position: 'relative', |
96c10d
|
79 |
overflowY: 'auto', |
2f6253
|
80 |
}; |
陈 |
81 |
}); |
|
82 |
|
|
83 |
// 是否透明化左侧一级菜单 |
|
84 |
const transparentMenuClass = computed(() => { |
|
85 |
const { type } = props; |
|
86 |
const { mode } = menuState; |
96c10d
|
87 |
const cls: string[] = []; |
2f6253
|
88 |
if ( |
陈 |
89 |
(type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) || |
|
90 |
props.appendClass |
|
91 |
) { |
96c10d
|
92 |
cls.push('basic-menu__sidebar-hor'); |
2f6253
|
93 |
} |
96c10d
|
94 |
|
V |
95 |
if (!props.isTop && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) { |
|
96 |
cls.push('basic-menu__second'); |
|
97 |
} |
|
98 |
return cls; |
2f6253
|
99 |
}); |
陈 |
100 |
|
|
101 |
watch( |
8a1bfd
|
102 |
() => currentRoute.value.name, |
2f6253
|
103 |
(name: string) => { |
96c10d
|
104 |
if (name === 'Redirect') return; |
V |
105 |
handleMenuChange(); |
|
106 |
props.isTop && appStore.getProjectConfig.menuSetting.split && getParentPath(); |
2f6253
|
107 |
} |
陈 |
108 |
); |
5f2a92
|
109 |
|
N |
110 |
watch( |
|
111 |
() => props.items, |
|
112 |
() => { |
|
113 |
if (props.items) { |
|
114 |
handleMenuChange(); |
|
115 |
} |
|
116 |
}, |
|
117 |
{ |
|
118 |
immediate: true, |
2f6253
|
119 |
} |
5f2a92
|
120 |
); |
2f6253
|
121 |
|
陈 |
122 |
async function getParentPath() { |
|
123 |
const { appendClass } = props; |
|
124 |
if (!appendClass) return ''; |
|
125 |
const parentPath = await getCurrentParentPath(unref(currentRoute).path); |
|
126 |
currentParentPath.value = parentPath; |
|
127 |
} |
|
128 |
|
|
129 |
async function handleMenuClick(menu: MenuType) { |
|
130 |
const { beforeClickFn } = props; |
|
131 |
if (beforeClickFn && isFunction(beforeClickFn)) { |
|
132 |
const flag = await beforeClickFn(menu); |
|
133 |
if (!flag) { |
|
134 |
return; |
|
135 |
} |
|
136 |
} |
|
137 |
const { path } = menu; |
|
138 |
menuState.selectedKeys = [path]; |
|
139 |
emit('menuClick', menu); |
|
140 |
} |
f64568
|
141 |
|
2f6253
|
142 |
function handleMenuChange() { |
陈 |
143 |
const { flatItems } = props; |
|
144 |
if (!unref(flatItems) || flatItems.length === 0) { |
|
145 |
return; |
|
146 |
} |
|
147 |
const findMenu = flatItems.find((menu) => menu.path === unref(currentRoute).path); |
|
148 |
if (findMenu) { |
|
149 |
if (menuState.mode !== MenuModeEnum.HORIZONTAL) { |
|
150 |
setOpenKeys(findMenu); |
|
151 |
} |
|
152 |
menuState.selectedKeys = [findMenu.path]; |
|
153 |
} else { |
|
154 |
resetKeys(); |
|
155 |
} |
|
156 |
} |
66acb2
|
157 |
|
V |
158 |
const showTitle = computed(() => { |
96c10d
|
159 |
return props.collapsedShowTitle && menuStore.getCollapsedState; |
66acb2
|
160 |
}); |
V |
161 |
|
2f6253
|
162 |
// render menu item |
陈 |
163 |
function renderMenuItem(menuList?: MenuType[], index = 1) { |
96c10d
|
164 |
if (!menuList) return; |
2f6253
|
165 |
const { appendClass } = props; |
陈 |
166 |
const levelCls = `basic-menu-item__level${index} ${menuState.theme} `; |
|
167 |
return menuList.map((menu) => { |
|
168 |
if (!menu) { |
|
169 |
return null; |
|
170 |
} |
|
171 |
|
|
172 |
const isAppendActiveCls = |
|
173 |
appendClass && index === 1 && menu.path === unref(currentParentPath); |
|
174 |
// 没有子节点 |
|
175 |
if (!menuHasChildren(menu)) { |
|
176 |
return ( |
|
177 |
<Menu.Item |
|
178 |
key={menu.path} |
|
179 |
class={`${levelCls}${isAppendActiveCls ? ' top-active-menu ' : ''}`} |
|
180 |
onClick={handleMenuClick.bind(null, menu)} |
|
181 |
> |
|
182 |
{() => [ |
|
183 |
<MenuContent |
|
184 |
item={menu} |
|
185 |
level={index} |
5737e4
|
186 |
showTitle={unref(showTitle)} |
2f6253
|
187 |
searchValue={menuState.searchValue} |
陈 |
188 |
/>, |
|
189 |
]} |
|
190 |
</Menu.Item> |
|
191 |
); |
|
192 |
} |
|
193 |
return ( |
|
194 |
<Menu.SubMenu key={menu.path} class={levelCls}> |
|
195 |
{{ |
|
196 |
title: () => [ |
|
197 |
<MenuContent |
5737e4
|
198 |
showTitle={unref(showTitle)} |
2f6253
|
199 |
item={menu} |
陈 |
200 |
level={index} |
|
201 |
searchValue={menuState.searchValue} |
|
202 |
/>, |
|
203 |
], |
|
204 |
default: () => renderMenuItem(menu.children, index + 1), |
|
205 |
}} |
|
206 |
</Menu.SubMenu> |
|
207 |
); |
|
208 |
}); |
|
209 |
} |
|
210 |
|
|
211 |
function renderMenu() { |
|
212 |
const isInline = props.mode === MenuModeEnum.INLINE; |
|
213 |
const { selectedKeys, defaultSelectedKeys, mode, theme } = menuState; |
|
214 |
|
|
215 |
const inlineCollapsedObj = isInline |
|
216 |
? props.isAppMenu |
|
217 |
? { |
|
218 |
inlineCollapsed: menuStore.getCollapsedState, |
|
219 |
} |
|
220 |
: { inlineCollapsed: props.inlineCollapsed } |
|
221 |
: {}; |
|
222 |
return ( |
|
223 |
<Menu |
77db3d
|
224 |
forceSubMenuRender={props.isAppMenu} |
2f6253
|
225 |
selectedKeys={selectedKeys} |
陈 |
226 |
defaultSelectedKeys={defaultSelectedKeys} |
|
227 |
mode={mode} |
|
228 |
openKeys={unref(getOpenKeys)} |
|
229 |
inlineIndent={props.inlineIndent} |
|
230 |
theme={unref(theme)} |
|
231 |
onOpenChange={handleOpenChange} |
5737e4
|
232 |
class={[ |
V |
233 |
'basic-menu', |
|
234 |
props.collapsedShowTitle && 'collapsed-show-title', |
96c10d
|
235 |
...unref(transparentMenuClass), |
5737e4
|
236 |
]} |
2f6253
|
237 |
{...inlineCollapsedObj} |
陈 |
238 |
> |
|
239 |
{{ |
|
240 |
default: () => renderMenuItem(props.items, 1), |
|
241 |
}} |
|
242 |
</Menu> |
|
243 |
); |
|
244 |
} |
|
245 |
|
|
246 |
onMounted(async () => { |
|
247 |
getParentPath(); |
|
248 |
}); |
96c10d
|
249 |
|
2f6253
|
250 |
return () => { |
陈 |
251 |
const { getCollapsedState } = menuStore; |
|
252 |
const { mode } = props; |
|
253 |
return mode === MenuModeEnum.HORIZONTAL ? ( |
|
254 |
renderMenu() |
|
255 |
) : ( |
66acb2
|
256 |
<section class={[`basic-menu-wrap`, !unref(showTitle) && 'hide-title']}> |
2f6253
|
257 |
{getSlot(slots, 'header')} |
beb4c3
|
258 |
<SearchInput |
N |
259 |
class={!props.search ? 'hidden' : ''} |
|
260 |
theme={props.theme} |
|
261 |
onChange={handleInputChange} |
|
262 |
onClick={handleInputClick} |
|
263 |
collapsed={getCollapsedState} |
|
264 |
/> |
96c10d
|
265 |
<section style={unref(getMenuWrapStyle)} class="basic-menu__content"> |
31e271
|
266 |
{renderMenu()} |
2f6253
|
267 |
</section> |
陈 |
268 |
</section> |
|
269 |
); |
|
270 |
}; |
|
271 |
}, |
|
272 |
}); |