import { ref, computed, watch, nextTick, onMounted } from 'vue' import { useI18n } from 'vue-i18n' import Config from '@/config/index' import { localesList } from '@/locale/index' import { useWindowWidth } from '@/composables/useWindowWidth' import useGlobalStore from '@/stores/use-global-store' import useRouter from '@/hooks/useRouter' import useRoute from '@/hooks/useRoute' export interface MenuItem { path: string label: string icon: string children?: MenuItem[] isOpenMenu?: boolean submenuHeight?: number isExternal?: boolean type?: string lang?: string } function cloneMenu(menus: MenuItem[]): MenuItem[] { return menus.map(item => ({ ...item, children: item.children ? cloneMenu(item.children) : [], isOpenMenu: item.isOpenMenu ?? false, })) } export function useMenuSplit() { const { locale } = useI18n() const globalStore = useGlobalStore() const mode = computed(() => globalStore.mode) const windowWidth = useWindowWidth(300) const shouldShowLanguageMenu = computed(() => windowWidth.value <= 991) const { host } = Config const router = useRouter() const route = useRoute() // 子菜单 DOM 引用 const submenuRefs = ref([]) function setSubmenuRef(index: number, el: HTMElement | null) { if (el) { submenuRefs.value[index] = el } } // 设置全局模式 function setMode(code: string) { globalStore.setMode(code); const homePath = mode.value === 'customer' ? '/pages/customer/index' : '/pages/ib/index' router.reLaunch(homePath) if (code === 'ib') { uni.$emit('open-ib') } nextTick(() => { submenuRefs.value = [] // 重置,等待重新绑定 menus.value.forEach((item, index) => { if (item.isOpenMenu && item.children && item.children.length) { updateSubmenuHeight(index) } }) }) } // 测量元素实际高度(用于过渡动画) const measureHeight = (element: HTMLElement): number => { const originalDisplay = element.style.display const originalPosition = element.style.position const originalVisibility = element.style.visibility const originalWidth = element.style.width element.style.display = 'block' element.style.position = 'absolute' element.style.visibility = 'hidden' element.style.width = '100%' const height = element.scrollHeight || element.offsetHeight element.style.display = originalDisplay element.style.position = originalPosition element.style.visibility = originalVisibility element.style.width = originalWidth return height } // 更新指定索引的子菜单高度 const updateSubmenuHeight = (index: number) => { const refs = submenuRefs.value nextTick(() => { if (refs && refs[index]) { const el = refs[index].$el || refs[index] const height = measureHeight(el) if (height > 0) { menus.value[index].submenuHeight = height } } }) } let clickTimer: ReturnType | null = null // 点击菜单项(切换展开/折叠或跳转) function handleClick(index: number) { if (clickTimer) return clickTimer = setTimeout(() => { clickTimer = null }, 300) const item = menus.value[index] // 无子菜单:执行跳转或特殊操作 if (!item.children || item.children.length === 0) { // #ifdef H5 if (item.type === 'chat') { if (window.LiveChatWidget) { window.LiveChatWidget.call('maximize') } return } // #endif router.push(item.path) return } // 有子菜单:切换展开/折叠状态 item.isOpenMenu = !item.isOpenMenu if (item.isOpenMenu) { nextTick(() => updateSubmenuHeight(index)) } } // 子菜单项点击事件(由 cwg-submenu 组件发出) function handleSubmenuClick(subItem: any) { // 处理语言切换 if (subItem.type === 'lang') { locale.value = subItem.lang return } // 处理外部链接 if (subItem.isExternal) { // #ifdef H5 window.open(subItem.path, '_blank') // #endif // #ifdef APP-PLUS plus.runtime.openURL(subItem.path) // #endif return } // 内部页面跳转 router.push(subItem.path) } // 窗口大小变化时重新计算所有已展开子菜单的高度 const handleResize = () => { menus.value.forEach((item, index) => { if (item.isOpenMenu && item.children && item.children.length) { updateSubmenuHeight(index) } }) } const customMenuList = computed(() => localesList.map((code) => ({ label: `language.${code}`, lang: code, type: "lang", path: '/' })) ) const languageMenuItem = computed(() => ({ path: '/', isOpenMenu: false, label: 'language.index', icon: 'cwg-lang', children: customMenuList.value, submenuHeight: 0, })) const customerBaseMenus = computed(() => [ { isOpenMenu: false, submenuHeight: 0, path: '/', label: 'Shop.Index.Transaction', icon: 'crm-trade', children: [ { path: '/pages/customer/index', label: 'Custom.Index.AccountList', icon: 'icon-client' }, { path: '/pages/customer/trade-history', label: 'Ib.Report.Tit1', icon: 'icon-transfer' }, { path: '/pages/customer/trade-position', label: 'Ib.Report.Tit4', icon: 'icon-transfer' }, { path: '/pages/customer/recording-history', label: 'Home.page_customer.item7', icon: 'icon-application' }, ], }, { isOpenMenu: false, submenuHeight: 0, path: '/', label: 'vu.item6', icon: 'crm-payment', children: [ { path: '/pages/customer/deposit', label: 'Home.page_customer.item2', icon: 'icon-deposit' }, { path: '/pages/customer/withdrawal', label: 'Home.page_customer.item3', icon: 'icon-withdrawal' }, { path: '/pages/customer/payment-history', label: 'Home.page_customer.item4', icon: 'icon-payment' }, { path: '/pages/customer/transfer', label: 'Custom.Index.Transfer', icon: 'icon-transfer' }, ], }, { path: '/pages/activities/index', isOpenMenu: false, label: 'Home.page_customer.item6', icon: 'crm-hd', children: [], }, { path: '/', isOpenMenu: false, label: 'vu.item5', icon: 'crm-chart-area', children: [ { path: '/pages/analytics/analystViews', label: 'News.Announcement', icon: 'icon-application' }, { path: '/pages/analytics/news', label: 'News.NewsInformation', icon: 'icon-application' }, { path: `https://www.${host}.com/${locale.value}/economic-calendar`, label: 'News.FinancialCalendar', icon: 'icon-application', isExternal: true, }, ], }, { path: '/pages/common/download', isOpenMenu: false, label: 'Downloadpage.item1', icon: 'crm-download', children: [], }, { path: '/pages/common/chat', isOpenMenu: false, label: 'Downloadpage.item16', icon: 'crm-headset', children: [], type: 'chat', }, { path: '/', isOpenMenu: false, label: 'Custom.Index.Settings', icon: 'crm-sz', children: [ { path: '/pages/mine/info?type=1', label: 'PersonalManagement.Title.PersonalInformation', icon: 'crm-headset' }, { path: '/pages/mine/info?type=2', label: 'PersonalManagement.Title.BankInformation', icon: 'crm-headset' }, { path: '/pages/mine/info?type=3', label: 'PersonalManagement.Title.FileManagement', icon: 'crm-headset' }, { path: '/pages/mine/info?type=4', label: 'PersonalManagement.Title.SecurityCenter', icon: 'crm-headset' }, { path: '/pages/common/notice', label: 'News.Notice', icon: 'crm-headset' }, ], }, ]) const ibBaseMenus = computed(() => [ { isOpenMenu: false, path: '/pages/ib/index', label: 'Documentary.console.item1', icon: 'crm-mb', }, { path: '/', label: 'Ib.Custom.Manage3', icon: 'crm-bg', children: [ { path: '/pages/ib/customer', label: 'Ib.Custom.Manage3', icon: 'icon-deposit' }, { path: '/pages/ib/subsList', label: 'Ib.Custom.Manage2', icon: 'icon-deposit' }, { path: '/pages/ib/agentList', label: 'Documentary.console.item23', icon: 'icon-deposit' }, { path: '/pages/ib/accountList', label: 'Ib.Custom.Manage1', icon: 'icon-deposit' } ], }, { isOpenMenu: false, submenuHeight: 0, path: '/', label: 'vu.item6', icon: 'crm-payment', children: [ { path: '/pages/ib/transfer', label: 'Home.page_ib.item4', icon: 'icon-payment' }, { path: '/pages/ib/withdraw', label: 'Home.page_ib.item5', icon: 'icon-transfer' }, { path: '/pages/ib/agent-transfer', label: 'Home.page_ib.item9', icon: 'icon-transfer' }, { path: '/pages/ib/recording', label: 'Home.page_ib.item7', icon: 'icon-application' }, ], }, { isOpenMenu: false, path: '/', label: 'Home.page_ib.item3', icon: 'crm-newspaper', children: [ { path: '/pages/ib/report', label: 'Home.page_ib.item3', icon: 'icon-withdrawal' }, ], }, ]) const menus = ref([]) // 监听 mode 变化,自动导航到对应首页 watch(mode, (newMode, oldMode) => { if (newMode !== oldMode) { const base = newMode === 'customer' ? [...customerBaseMenus.value] : [...ibBaseMenus.value] if (shouldShowLanguageMenu.value) { base.push(languageMenuItem.value) } menus.value = cloneMenu(base) } }, { immediate: true }) // 监听路由变化:自动展开包含当前路径的父菜单,不自动关闭其他菜单 watch(route, () => { const currentPath = route.path const shouldOpenIndices: number[] = [] menus.value.forEach((item, idx) => { if (item.children && item.children.length) { const isActive = item.children.some(child => { if (child.isExternal || child.type === 'lang') return false return currentPath === child.path || currentPath.startsWith(child.path + '?') || currentPath.startsWith(child.path + '/') }) if (isActive && !item.isOpenMenu) { shouldOpenIndices.push(idx) } } }) if (shouldOpenIndices.length) { shouldOpenIndices.forEach(idx => { menus.value[idx].isOpenMenu = true }) nextTick(() => { shouldOpenIndices.forEach(idx => updateSubmenuHeight(idx)) }) } }, { immediate: true }) watch(windowWidth, handleResize) onMounted(() => { nextTick(() => { menus.value.forEach((item, index) => { if (item.isOpenMenu && item.children && item.children.length) { updateSubmenuHeight(index) } }) }) }) return { menus, // 最终菜单(已克隆,可直接修改 isOpenMenu 等) mode, // 只读或按需使用 shouldShowLanguageMenu, // 可选,供外部获取状态 windowWidth, // 可选,供外部获取宽度(300) setMode, // 可选,供外部设置模式 setSubmenuRef, // 可选,供外部设置子菜单引用 updateSubmenuHeight, // 可选,供外部更新子菜单高度 handleClick, // 可选,供外部处理点击事件 handleSubmenuClick, // 可选,供外部处理子菜单点击事件 } }