useMenuSplit.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. import { ref, computed, watch, nextTick, onMounted } from 'vue'
  2. import { useI18n } from 'vue-i18n'
  3. import Config from '@/config/index'
  4. import { localesList } from '@/locale/index'
  5. import { useWindowWidth } from '@/composables/useWindowWidth'
  6. import useGlobalStore from '@/stores/use-global-store'
  7. import useRouter from '@/hooks/useRouter'
  8. import useRoute from '@/hooks/useRoute'
  9. import LiveChatService from '@/utils/liveChat.js'
  10. export interface MenuItem {
  11. path: string
  12. label: string
  13. icon: string
  14. children?: MenuItem[]
  15. isOpenMenu?: boolean
  16. submenuHeight?: number
  17. isExternal?: boolean
  18. type?: string
  19. lang?: string
  20. }
  21. function cloneMenu(menus: MenuItem[]): MenuItem[] {
  22. return menus.map(item => ({
  23. ...item,
  24. children: item.children ? cloneMenu(item.children) : [],
  25. isOpenMenu: item.isOpenMenu ?? false,
  26. // 🔥 直接按数量 × 40px
  27. submenuHeight: (item.children?.length || 0) * ((40 + 8)) + 8,
  28. }))
  29. }
  30. export function useMenuSplit(handleClick1: (item: MenuItem) => void) {
  31. const { locale } = useI18n()
  32. const globalStore = useGlobalStore()
  33. const mode = computed(() => globalStore.mode)
  34. const windowWidth = useWindowWidth(300)
  35. const shouldShowLanguageMenu = computed(() => windowWidth.value <= 991)
  36. const { host } = Config
  37. const router = useRouter()
  38. const route = useRoute()
  39. const submenuRefs = ref<any[]>([])
  40. function setSubmenuRef(index: number, el: HTMLElement | null) { }
  41. // 设置全局模式
  42. function setMode(code: string) {
  43. globalStore.setMode(code);
  44. switch (code) {
  45. case 'follow':
  46. router.reLaunch('/pages/follow/index')
  47. break
  48. case 'ib':
  49. router.reLaunch('/pages/ib/index')
  50. break
  51. case 'customer':
  52. // router.reLaunch('/pages/customer/dashboard')
  53. router.reLaunch('/pages/customer/index')
  54. break
  55. default:
  56. break
  57. }
  58. if (code === 'ib') {
  59. uni.$emit('open-ib')
  60. }
  61. }
  62. // 🔥 已删除:measureHeight
  63. // 🔥 已删除:updateSubmenuHeight
  64. let clickTimer: ReturnType<typeof setTimeout> | null = null
  65. function handleClick(index: number) {
  66. if (clickTimer) return
  67. clickTimer = setTimeout(() => {
  68. clickTimer = null
  69. }, 300)
  70. const item = menus.value[index]
  71. if (!item.children || item.children.length === 0) {
  72. // #ifdef H5
  73. if (item.type === 'chat' && !shouldShowLanguageMenu.value) {
  74. if (LiveChatService) {
  75. LiveChatService.showChat();
  76. }
  77. return
  78. } else {
  79. shouldShowLanguageMenu.value && handleClick1(item)
  80. router.push(item.path)
  81. return
  82. }
  83. // #endif
  84. shouldShowLanguageMenu.value && handleClick1(item)
  85. router.push(item.path)
  86. return
  87. }
  88. item.isOpenMenu = !item.isOpenMenu
  89. }
  90. function handleSubmenuClick(subItem: any) {
  91. if (subItem.type === 'lang') {
  92. locale.value = subItem.lang
  93. return
  94. }
  95. if (subItem.isExternal) {
  96. // #ifdef H5
  97. window.open(subItem.path, '_blank')
  98. // #endif
  99. // #ifdef APP-PLUS
  100. plus.runtime.openURL(subItem.path)
  101. // #endif
  102. return
  103. }
  104. shouldShowLanguageMenu.value && handleClick1(subItem)
  105. router.push(subItem.path)
  106. }
  107. const handleResize = () => { }
  108. const customMenuList = computed(() =>
  109. localesList.map((code) => ({
  110. label: `language.${code}`,
  111. lang: code,
  112. type: "lang",
  113. path: '/'
  114. }))
  115. )
  116. const languageMenuItem = computed<MenuItem>(() => ({
  117. path: '/',
  118. isOpenMenu: false,
  119. label: 'language.index',
  120. icon: 'cwg-lang',
  121. children: customMenuList.value,
  122. submenuHeight: customMenuList.value.length * ((40 + 8)) + 8,
  123. }))
  124. const customerBaseMenus = computed<MenuItem[]>(() => [
  125. // {
  126. // isOpenMenu: false,
  127. // path: '/pages/customer/dashboard',
  128. // label: 'Documentary.console.item1',
  129. // icon: 'crm-mb',
  130. // submenuHeight: 0,
  131. // },
  132. {
  133. isOpenMenu: false,
  134. submenuHeight: 4 * ((40 + 8)) + 8,
  135. path: '/',
  136. label: 'Shop.Index.Transaction',
  137. icon: 'crm-trade',
  138. children: [
  139. { path: '/pages/customer/index', label: 'Custom.Index.AccountList', icon: 'icon-client' },
  140. { path: '/pages/customer/trade-history', label: 'Ib.Report.Tit1', icon: 'icon-transfer' },
  141. { path: '/pages/customer/trade-position', label: 'Ib.Report.Tit4', icon: 'icon-transfer' },
  142. { path: '/pages/customer/recording-history', label: 'Home.page_customer.item7', icon: 'icon-application' },
  143. ],
  144. },
  145. {
  146. isOpenMenu: false,
  147. submenuHeight: 6 * ((40 + 8)) + 8,
  148. path: '/',
  149. label: 'vu.item6',
  150. icon: 'crm-payment',
  151. children: [
  152. { path: '/pages/customer/deposit-select', label: 'Home.page_customer.item2', icon: 'icon-deposit' },
  153. { path: '/pages/customer/withdrawal-select', label: 'Home.page_customer.item3', icon: 'icon-withdrawal' },
  154. { path: '/pages/customer/payment-history', label: 'Home.page_customer.item4', icon: 'icon-payment' },
  155. { path: '/pages/customer/transfer', label: 'Custom.Index.Transfer', icon: 'icon-transfer' },
  156. { path: '/pages/customer/wallet-transfer', label: 'wallet.item62', icon: 'icon-transfer' },
  157. { path: '/pages/customer/wallet-history', label: 'wallet.item7', icon: 'icon-transfer' },
  158. ],
  159. },
  160. {
  161. path: '/pages/activities/index',
  162. isOpenMenu: false,
  163. label: 'Home.page_customer.item6',
  164. icon: 'crm-hd',
  165. children: [],
  166. submenuHeight: 0,
  167. },
  168. {
  169. path: '/',
  170. isOpenMenu: false,
  171. submenuHeight: 2 * ((40 + 8)) + 8,
  172. label: 'vu.item5',
  173. icon: 'crm-chart-area',
  174. children: [
  175. { path: '/pages/analytics/analystViews', label: 'News.Announcement', icon: 'icon-application' },
  176. { path: '/pages/analytics/news', label: 'News.NewsInformation', icon: 'icon-application' },
  177. ],
  178. },
  179. {
  180. path: '/pages/common/download',
  181. isOpenMenu: false,
  182. label: 'Downloadpage.item1',
  183. icon: 'crm-download',
  184. children: [],
  185. submenuHeight: 0,
  186. },
  187. {
  188. path: '/pages/common/chat',
  189. isOpenMenu: false,
  190. label: 'Downloadpage.item16',
  191. icon: 'crm-headset',
  192. children: [],
  193. type: 'chat',
  194. submenuHeight: 0,
  195. },
  196. {
  197. path: '/',
  198. isOpenMenu: false,
  199. submenuHeight: 5 * ((40 + 8)) + 8,
  200. label: 'Custom.Index.Settings',
  201. icon: 'crm-sz',
  202. children: [
  203. { path: '/pages/mine/info?type=1', label: 'PersonalManagement.Title.PersonalInformation', icon: 'crm-headset' },
  204. { path: '/pages/mine/info?type=2', label: 'PersonalManagement.Title.BankInformation', icon: 'crm-headset' },
  205. { path: '/pages/mine/info?type=3', label: 'PersonalManagement.Title.FileManagement', icon: 'crm-headset' },
  206. { path: '/pages/mine/info?type=4', label: 'PersonalManagement.Title.SecurityCenter', icon: 'crm-headset' },
  207. { path: '/pages/common/notice', label: 'News.Notice', icon: 'crm-headset' },
  208. ],
  209. },
  210. ])
  211. const ibBaseMenus = computed<MenuItem[]>(() => [
  212. {
  213. isOpenMenu: false,
  214. path: '/pages/ib/index',
  215. label: 'Documentary.console.item1',
  216. icon: 'crm-mb',
  217. submenuHeight: 0,
  218. },
  219. {
  220. path: '/',
  221. label: 'Ib.Custom.Manage3',
  222. icon: 'crm-bg',
  223. submenuHeight: 4 * ((40 + 8)) + 8,
  224. children: [
  225. { path: '/pages/ib/customer', label: 'Ib.Custom.Manage3', icon: 'icon-deposit' },
  226. { path: '/pages/ib/subsList', label: 'Ib.Custom.Manage2', icon: 'icon-deposit' },
  227. { path: '/pages/ib/agentList', label: 'Documentary.console.item23', icon: 'icon-deposit' },
  228. { path: '/pages/ib/accountList', label: 'Ib.Custom.Manage1', icon: 'icon-deposit' }
  229. ],
  230. },
  231. {
  232. isOpenMenu: false,
  233. submenuHeight: 4 * ((40 + 8)) + 8,
  234. path: '/',
  235. label: 'vu.item6',
  236. icon: 'crm-payment',
  237. children: [
  238. { path: '/pages/ib/transfer', label: 'Home.page_ib.item4', icon: 'icon-payment' },
  239. { path: '/pages/ib/withdraw-select', label: 'Home.page_ib.item5', icon: 'icon-transfer' },
  240. { path: '/pages/ib/agent-transfer', label: 'Home.page_ib.item9', icon: 'icon-transfer' },
  241. { path: '/pages/ib/recording', label: 'Home.page_ib.item7', icon: 'icon-application' },
  242. ],
  243. },
  244. {
  245. isOpenMenu: false,
  246. submenuHeight: 2 * ((40 + 8)) + 8,
  247. path: '/',
  248. label: 'Home.page_ib.item3',
  249. icon: 'crm-newspaper',
  250. children: [
  251. // { path: '/pages/ib/report', label: 'Home.page_ib.item3', icon: 'icon-withdrawal' },
  252. { path: '/pages/ib/complexReport', label: 'Home.page_ib.item11', icon: 'icon-withdrawal' },
  253. { path: '/pages/ib/report?type=1', label: 'Ib.Report.Title1', icon: 'icon-withdrawal' },
  254. { path: '/pages/ib/report?type=2', label: 'Ib.Report.Title2', icon: 'icon-withdrawal' },
  255. { path: '/pages/ib/report?type=3', label: 'Ib.Report.Title3', icon: 'icon-withdrawal' },
  256. { path: '/pages/ib/report?type=6', label: 'news_add_field.IbReport.Title6', icon: 'icon-withdrawal' },
  257. ],
  258. },
  259. // 推广
  260. // {
  261. // isOpenMenu: false,
  262. // path: '/',
  263. // label: 'Home.page_ib.item12',
  264. // icon: 'crm-bulletin',
  265. // submenuHeight: 1 * ((40 + 8)) + 8,
  266. // children: [
  267. // { path: '/pages/ib/promotion', label: 'Home.page_ib.item13', icon: 'icon-withdrawal' },
  268. // ],
  269. // },
  270. ])
  271. const followBaseMenus = computed<MenuItem[]>(() => [
  272. {
  273. isOpenMenu: false,
  274. path: '/pages/follow/index',
  275. label: 'Documentary.console.item1',
  276. icon: 'crm-mb',
  277. submenuHeight: 0,
  278. },
  279. {
  280. isOpenMenu: false,
  281. path: '/pages/follow/trading-center',
  282. label: 'Documentary.page_doc.item2',
  283. icon: 'crm-gd',
  284. submenuHeight: 0,
  285. },
  286. {
  287. isOpenMenu: false,
  288. submenuHeight: 1 * ((40 + 8)) + 8,
  289. path: '/',
  290. label: 'Documentary.page_doc.item3',
  291. icon: 'crm-newspaper',
  292. children: [
  293. { path: '/pages/follow/report', label: 'Documentary.page_doc.item3', icon: 'icon-client' },
  294. ],
  295. },
  296. {
  297. isOpenMenu: false,
  298. submenuHeight: 2 * ((40 + 8)) + 8,
  299. path: '/',
  300. label: 'Documentary.page_doc.item4',
  301. icon: 'crm-payment',
  302. children: [
  303. { path: '/pages/follow/transfer', label: 'Documentary.TundManagement.item2', icon: 'icon-client' },
  304. { path: '/pages/follow/transfer-history', label: 'Documentary.TundManagement.item3', icon: 'icon-transfer' }
  305. ],
  306. },
  307. {
  308. isOpenMenu: false,
  309. submenuHeight: 5 * ((40 + 8)) + 8,
  310. path: '/',
  311. label: 'Documentary.page_doc.item5',
  312. icon: 'crm-trade',
  313. children: [
  314. { path: '/pages/follow/trading-management', label: 'Documentary.TundManagement.item11', icon: 'icon-client' },
  315. { path: '/pages/follow/follow-list', label: 'Documentary.TundManagement.item17', icon: 'icon-client' },
  316. { path: '/pages/follow/account-management', label: 'Documentary.TundManagement.item9', icon: 'icon-transfer' },
  317. { path: '/pages/follow/subscribe-list', label: 'Documentary.TundManagement.item45', icon: 'icon-transfer' },
  318. { path: '/pages/follow/record', label: 'Documentary.TundManagement.item10', icon: 'icon-transfer' }
  319. ],
  320. },
  321. ])
  322. const menus = ref<MenuItem[]>([])
  323. watch(mode, (newMode, oldMode) => {
  324. if (newMode !== oldMode) {
  325. let base: MenuItem[] = []
  326. switch (newMode) {
  327. case 'follow': base = [...followBaseMenus.value]; break
  328. case 'ib': base = [...ibBaseMenus.value]; break
  329. case 'customer': base = [...customerBaseMenus.value]; break
  330. default: break
  331. }
  332. if (shouldShowLanguageMenu.value) base.push(languageMenuItem.value)
  333. menus.value = cloneMenu(base)
  334. }
  335. }, { immediate: true })
  336. watch(route, () => {
  337. const currentPath = route.path
  338. menus.value.forEach((item, idx) => {
  339. if (item.children?.length) {
  340. const isActive = item.children.some(child => {
  341. if (child.isExternal || child.type === 'lang') return false
  342. return currentPath === child.path || currentPath.startsWith(child.path + '?')
  343. })
  344. if (isActive) item.isOpenMenu = true
  345. }
  346. })
  347. }, { immediate: true })
  348. onMounted(() => { })
  349. return {
  350. menus,
  351. mode,
  352. shouldShowLanguageMenu,
  353. windowWidth,
  354. setMode,
  355. setSubmenuRef,
  356. handleClick,
  357. handleSubmenuClick,
  358. }
  359. }