useMenuSplit.ts 14 KB

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