|
|
@@ -0,0 +1,431 @@
|
|
|
+<template>
|
|
|
+ <aside class="home-sidebar" :class="{ 'is-collapsed': !sidebarStore.isOpen }">
|
|
|
+ <div class="sidebar-menu">
|
|
|
+ <div v-for="item in primaryItems" :key="item.key" class="sidebar-group">
|
|
|
+ <button class="sidebar-item" :class="{ active: isActive(item) }" type="button"
|
|
|
+ :title="sidebarStore.isOpen ? '' : getItemLabel(item)" @click="handleItemClick(item)">
|
|
|
+ <el-icon class="sidebar-icon">
|
|
|
+ <component :is="item.icon" />
|
|
|
+ </el-icon>
|
|
|
+ <span v-if="sidebarStore.isOpen" class="sidebar-label">{{ getItemLabel(item) }}</span>
|
|
|
+ <el-icon v-if="sidebarStore.isOpen && item.children?.length" class="sidebar-arrow"
|
|
|
+ :class="{ rotated: isExpanded(item.key) }" @click.stop="toggleSection(item.key)">
|
|
|
+ <ArrowDown />
|
|
|
+ </el-icon>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <div v-if="sidebarStore.isOpen && item.children?.length && isExpanded(item.key)" class="sidebar-submenu">
|
|
|
+ <button v-for="child in item.children" :key="child.key" class="sidebar-subitem"
|
|
|
+ :class="{ active: isActive(child) }" type="button" :title="getItemLabel(child)"
|
|
|
+ @click="handleItemClick(child)">
|
|
|
+ <span class="sidebar-sub-label">{{ getItemLabel(child) }}</span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+ <button class="sidebar-toggle" type="button" :aria-label="sidebarStore.isOpen ? t('收起') : t('展开')"
|
|
|
+ @click="sidebarStore.setSheetOpen(!sidebarStore.isOpen)">
|
|
|
+ <el-icon>
|
|
|
+ <component :is="sidebarStore.isOpen ? ArrowLeft : ArrowRight" />
|
|
|
+ </el-icon>
|
|
|
+ </button>
|
|
|
+ </aside>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { computed, onMounted, ref, watch } from 'vue';
|
|
|
+import { useRoute, useRouter } from 'vue-router';
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+import {
|
|
|
+ ArrowDown,
|
|
|
+ ArrowLeft,
|
|
|
+ ArrowRight,
|
|
|
+ Collection,
|
|
|
+ Discount,
|
|
|
+ Document,
|
|
|
+ Football,
|
|
|
+ House,
|
|
|
+ Monitor,
|
|
|
+ Money,
|
|
|
+ Ship,
|
|
|
+ Ticket,
|
|
|
+ Trophy,
|
|
|
+ User,
|
|
|
+ Wallet,
|
|
|
+} from '@element-plus/icons-vue';
|
|
|
+import { useHomeLeftDrawStore } from '@/store/homeLeftDraw';
|
|
|
+import { useGameIframeStore } from '@/store/gameIframe';
|
|
|
+import { useUserStore } from '@/store/user/index.js';
|
|
|
+import { getBalance, getDeomUrl, getGameUrl, getPlatforms } from '@/request/api.js';
|
|
|
+
|
|
|
+const route = useRoute();
|
|
|
+const router = useRouter();
|
|
|
+const sidebarStore = useHomeLeftDrawStore();
|
|
|
+const gameIframeStore = useGameIframeStore();
|
|
|
+const userStore = useUserStore();
|
|
|
+const { t } = useI18n();
|
|
|
+
|
|
|
+const platformGroups = ref([]);
|
|
|
+
|
|
|
+const primaryItems = computed(() => {
|
|
|
+ const sports = getPlatformsByType(4);
|
|
|
+ const live = getPlatformsByType(1);
|
|
|
+ const casino = getPlatformsByType(7);
|
|
|
+
|
|
|
+ return [
|
|
|
+ { key: 'home', labelKey: '首页', icon: House, route: 'Home' },
|
|
|
+ {
|
|
|
+ key: 'sports',
|
|
|
+ labelKey: '体育',
|
|
|
+ icon: Football,
|
|
|
+ route: 'SportsBetting',
|
|
|
+ children: [
|
|
|
+ { key: 'f1-sports', label: 'f1bet体育', route: 'SportsBetting', query: { category: 'f1' } },
|
|
|
+ ...sports.map(platform => createPlatformItem(platform, 4)),
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'live',
|
|
|
+ labelKey: '真人',
|
|
|
+ icon: User,
|
|
|
+ route: 'videoGames',
|
|
|
+ query: { type: 1 },
|
|
|
+ children: live.map(platform => createPlatformItem(platform, 1)),
|
|
|
+ },
|
|
|
+ { key: 'fishing', labelKey: '捕鱼', icon: Ship, route: 'videoGames', query: { type: 6 } },
|
|
|
+ {
|
|
|
+ key: 'casino',
|
|
|
+ labelKey: '棋牌',
|
|
|
+ icon: Trophy,
|
|
|
+ children: casino.map(platform => createPlatformItem(platform, 7)),
|
|
|
+ },
|
|
|
+ { key: 'lottery', labelKey: '彩票', icon: Ticket, route: 'EntertainmentList' },
|
|
|
+ { key: 'games', labelKey: '电子', icon: Monitor, route: 'videoGames', query: { type: 2 } },
|
|
|
+ { key: 'promotion', labelKey: '优惠活动', icon: Discount, route: 'Promotion' },
|
|
|
+ { key: 'profile', labelKey: '我的', icon: User, route: 'My' },
|
|
|
+ { key: 'history', labelKey: '记录', icon: Document, route: 'rechargeRecord' },
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const expandedMenus = ref(['sports']);
|
|
|
+const sportsRouteNames = ['SportsBetting', 'SportDetail', 'WorldCup', 'TotalSoccerAdvance'];
|
|
|
+const lotteryRouteNames = [
|
|
|
+ 'EntertainmentList',
|
|
|
+ 'Entertainment',
|
|
|
+ 'ExtremeSpeed28',
|
|
|
+ 'Digital',
|
|
|
+ 'NewDigital',
|
|
|
+ 'SpeedDigital',
|
|
|
+ 'HKDigital',
|
|
|
+ 'JisuGame',
|
|
|
+ 'EntertainmentHistory',
|
|
|
+ 'DigitalOrderList',
|
|
|
+ 'DigitalOrderDetail',
|
|
|
+ 'EntertainmentDetail',
|
|
|
+ 'LotteryOrderDetail',
|
|
|
+];
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ try {
|
|
|
+ const res = await getPlatforms({ device: 'pc' });
|
|
|
+ platformGroups.value = res?.data?.list || [];
|
|
|
+
|
|
|
+ ['sports'].forEach(key => {
|
|
|
+ if (primaryItems.value.find(item => item.key === key)?.children?.length) {
|
|
|
+ expandedMenus.value = Array.from(new Set([...expandedMenus.value, key]));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取侧边栏游戏平台失败:', error);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => route.name,
|
|
|
+ (name) => {
|
|
|
+ if (sportsRouteNames.includes(name)) {
|
|
|
+ expandedMenus.value = Array.from(new Set([...expandedMenus.value, 'sports']));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true },
|
|
|
+);
|
|
|
+
|
|
|
+function isExpanded(key) {
|
|
|
+ return expandedMenus.value.includes(key);
|
|
|
+}
|
|
|
+
|
|
|
+function toggleSection(key) {
|
|
|
+ expandedMenus.value = isExpanded(key)
|
|
|
+ ? expandedMenus.value.filter((item) => item !== key)
|
|
|
+ : [...expandedMenus.value, key];
|
|
|
+}
|
|
|
+
|
|
|
+function getPlatformsByType(gameType) {
|
|
|
+ return platformGroups.value.find(item => Number(item.gameType) === gameType)?.platforms || [];
|
|
|
+}
|
|
|
+
|
|
|
+function createPlatformItem(platform, gameType) {
|
|
|
+ return {
|
|
|
+ key: `${gameType}-${platform.platType}`,
|
|
|
+ label: platform.name,
|
|
|
+ external: true,
|
|
|
+ platform,
|
|
|
+ gameType,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function getItemLabel(item) {
|
|
|
+ return item.label || t(item.labelKey);
|
|
|
+}
|
|
|
+
|
|
|
+function refreshBalance() {
|
|
|
+ return getBalance()
|
|
|
+ .then(res => {
|
|
|
+ if (res?.data?.money !== undefined) {
|
|
|
+ userStore.balance = res.data.money;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.error('获取游戏平台余额失败:', error);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function isActive(item) {
|
|
|
+ if (item.key === 'lottery') return lotteryRouteNames.includes(route.name);
|
|
|
+ if (item.key === 'sports') {
|
|
|
+ return sportsRouteNames.includes(route.name)
|
|
|
+ || route.query.category === 'f1'
|
|
|
+ || (route.name === 'videoGames' && String(route.query.type) === '4');
|
|
|
+ }
|
|
|
+ if (item.key === 'casino') {
|
|
|
+ return route.name === 'videoGames' && String(route.query.type) === '7';
|
|
|
+ }
|
|
|
+ if (item.external) {
|
|
|
+ return gameIframeStore.platformKey === item.key;
|
|
|
+ }
|
|
|
+ if (item.query?.category) return route.name === item.route && route.query.category === item.query.category;
|
|
|
+ if (item.route === 'videoGames' && item.query?.type !== undefined) {
|
|
|
+ return route.name === item.route && String(route.query.type) === String(item.query.type);
|
|
|
+ }
|
|
|
+ if (item.route === 'Home') return route.name === 'Home';
|
|
|
+ return route.name === item.route || route.path.toLowerCase().includes(String(item.route).toLowerCase());
|
|
|
+}
|
|
|
+
|
|
|
+function handleItemClick(item) {
|
|
|
+ if (item.key === 'sports') {
|
|
|
+ return toggleSection(item.key);
|
|
|
+ }
|
|
|
+ if (item.key === 'live') {
|
|
|
+ return toggleSection(item.key);
|
|
|
+ }
|
|
|
+ if (item.key === 'casino') {
|
|
|
+ return toggleSection(item.key);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.external) {
|
|
|
+ const params = {
|
|
|
+ platType: item.platform.platType,
|
|
|
+ gameType: item.gameType,
|
|
|
+ device: 'pc',
|
|
|
+ };
|
|
|
+ const request = localStorage.getItem('youke_token')
|
|
|
+ ? getDeomUrl(params)
|
|
|
+ : getGameUrl(params);
|
|
|
+
|
|
|
+ request.then(res => {
|
|
|
+ const url = res?.data?.url || res?.data?.data?.url || res?.url;
|
|
|
+ if (!url) return;
|
|
|
+
|
|
|
+ refreshBalance();
|
|
|
+ gameIframeStore.open(url, getItemLabel(item), item.key, item.gameType);
|
|
|
+ router.push({ name: 'videoGames', query: { type: item.gameType } });
|
|
|
+ }).catch(error => {
|
|
|
+ console.error('打开游戏平台失败:', error);
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (item.route === 'recharge') return router.push({ name: 'rechargeRecord' });
|
|
|
+ if (item.route === 'withdraw') return router.push({ name: 'withdrawalHistory' });
|
|
|
+ if (item.route === 'language') return;
|
|
|
+ if (item.route) router.push(item.query ? { name: item.route, query: item.query } : { name: item.route });
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.home-sidebar {
|
|
|
+ z-index: 12;
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 258px;
|
|
|
+ width: 258px;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100%;
|
|
|
+ padding: 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #1e1e1e;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
+ transition: flex-basis 0.25s ease, width 0.25s ease;
|
|
|
+ box-shadow: 2px 0 12px rgba(0, 0, 0, 0.35);
|
|
|
+ border-radius: 16px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.home-sidebar.is-collapsed {
|
|
|
+ flex-basis: 72px;
|
|
|
+ width: 72px;
|
|
|
+ align-items: center;
|
|
|
+ padding-inline: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-menu,
|
|
|
+.sidebar-footer {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-menu {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-footer {
|
|
|
+ padding-top: 10px;
|
|
|
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.home-sidebar.is-collapsed .sidebar-menu,
|
|
|
+.home-sidebar.is-collapsed .sidebar-footer {
|
|
|
+ width: 100%;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.home-sidebar.is-collapsed .sidebar-item {
|
|
|
+ justify-content: center;
|
|
|
+ width: 54px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-group {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-item,
|
|
|
+.sidebar-subitem {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 200px;
|
|
|
+ min-height: 46px;
|
|
|
+ padding: 0 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 10px;
|
|
|
+ background: transparent;
|
|
|
+ color: #e5e5e5;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ text-align: left;
|
|
|
+ transition: background 0.2s ease, color 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-item:hover,
|
|
|
+.sidebar-subitem:hover {
|
|
|
+ background: #2a2a2a;
|
|
|
+ color: #f8b932;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-item.active,
|
|
|
+.sidebar-subitem.active {
|
|
|
+ background: rgba(248, 185, 50, 0.14);
|
|
|
+ color: #f8b932;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-icon {
|
|
|
+ flex: 0 0 22px;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-label {
|
|
|
+ margin-left: 12px;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-arrow {
|
|
|
+ margin-left: auto;
|
|
|
+ font-size: 14px;
|
|
|
+ transition: transform 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-arrow.rotated {
|
|
|
+ transform: rotate(-180deg);
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-divider {
|
|
|
+ height: 1px;
|
|
|
+ margin: 8px;
|
|
|
+ background: rgba(255, 255, 255, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-submenu {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 2px 0 4px 14px;
|
|
|
+ margin-left: 10px;
|
|
|
+ border-left: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-subitem {
|
|
|
+ width: 182px;
|
|
|
+ min-height: 40px;
|
|
|
+ padding-left: 18px;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-sub-label {
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-toggle {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ right: -12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 24px;
|
|
|
+ height: 48px;
|
|
|
+ padding: 0;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.12);
|
|
|
+ border-radius: 0 8px 8px 0;
|
|
|
+ background: #1e1e1e;
|
|
|
+ color: #999;
|
|
|
+ cursor: pointer;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.35);
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-toggle:hover {
|
|
|
+ color: #f8b932;
|
|
|
+}
|
|
|
+
|
|
|
+@media screen and (max-width: 800px) {
|
|
|
+ .home-sidebar {
|
|
|
+ flex-basis: 72px;
|
|
|
+ width: 72px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .home-sidebar:not(.is-collapsed) {
|
|
|
+ position: relative;
|
|
|
+ width: 258px;
|
|
|
+ flex-basis: 258px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|