feat(榜单): 新增榜单页面

This commit is contained in:
hzj 2025-09-09 18:58:55 +08:00
parent 2a7ab3e733
commit 18724b011b
17 changed files with 652 additions and 427 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,206 +1,212 @@
import { createRouter, createWebHashHistory } from "vue-router"; import { createRouter, createWebHashHistory } from 'vue-router'
import { installRouteGuard } from "../utils/routeGuard.js"; import { installRouteGuard } from '../utils/routeGuard.js'
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [ routes: [
{ {
path: "/", path: '/',
name: "root", name: 'root',
component: () => import("../views/LoadingView.vue"), component: () => import('../views/LoadingView.vue'),
meta: { requiresAuth: false, isPublic: true }, meta: { requiresAuth: false, isPublic: true },
}, },
{ {
path: "/loading", path: '/loading',
name: "loading", name: 'loading',
component: () => import("../views/LoadingView.vue"), component: () => import('../views/LoadingView.vue'),
meta: { requiresAuth: false, isPublic: true }, meta: { requiresAuth: false, isPublic: true },
}, },
{ {
path: "/host-center", path: '/host-center',
name: "host-center", name: 'host-center',
component: () => import("../views/HostCenterView.vue"), component: () => import('../views/HostCenterView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/agency-center", path: '/agency-center',
name: "agency-center", name: 'agency-center',
component: () => import("../views/AgencyCenterView.vue"), component: () => import('../views/AgencyCenterView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/bd-center", path: '/bd-center',
name: "bd-center", name: 'bd-center',
component: () => import("../views/BDCenterView.vue"), component: () => import('../views/BDCenterView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/coin-seller", path: '/coin-seller',
name: "coin-seller", name: 'coin-seller',
component: () => import("../views/CoinSellerView.vue"), component: () => import('../views/CoinSellerView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/apply", path: '/apply',
name: "apply", name: 'apply',
component: () => import("../views/ApplyView.vue"), component: () => import('../views/ApplyView.vue'),
meta: { requiresAuth: false }, meta: { requiresAuth: false },
}, },
{ {
path: "/message", path: '/message',
name: "message", name: 'message',
component: () => import("../views/MessageView.vue"), component: () => import('../views/MessageView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/transfer", path: '/transfer',
name: "transfer", name: 'transfer',
component: () => import("../views/TransferView.vue"), component: () => import('../views/TransferView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/exchange-gold-coins", path: '/exchange-gold-coins',
name: "exchange-gold-coins", name: 'exchange-gold-coins',
component: () => import("../views/ExchangeGoldCoinsView.vue"), component: () => import('../views/ExchangeGoldCoinsView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
// 辅助页面 // 辅助页面
{ {
path: "/about", path: '/about',
name: "about", name: 'about',
component: () => import("../views/AboutView.vue"), component: () => import('../views/AboutView.vue'),
}, },
{ {
path: "/host-setting", path: '/host-setting',
name: "host-setting", name: 'host-setting',
component: () => import("../views/HostSettingView.vue"), component: () => import('../views/HostSettingView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/search-payee", path: '/search-payee',
name: "search-payee", name: 'search-payee',
component: () => import("../views/SearchPayeeView.vue"), component: () => import('../views/SearchPayeeView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/information-details", path: '/information-details',
name: "information-details", name: 'information-details',
component: () => import("../views/InformationDetailsView.vue"), component: () => import('../views/InformationDetailsView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/invite-members", path: '/invite-members',
name: "invite-members", name: 'invite-members',
component: () => import("../views/InviteMembersView.vue"), component: () => import('../views/InviteMembersView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/team-bill", path: '/team-bill',
name: "team-bill", name: 'team-bill',
component: () => import("../views/TeamBillView.vue"), component: () => import('../views/TeamBillView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/team-member", path: '/team-member',
name: "team-member", name: 'team-member',
component: () => import("../views/TeamMemberView.vue"), component: () => import('../views/TeamMemberView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/platform-policy", path: '/platform-policy',
name: "platform-policy", name: 'platform-policy',
component: () => import("../views/PlatformPolicyView.vue"), component: () => import('../views/PlatformPolicyView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/coin-seller-search", path: '/coin-seller-search',
name: "coin-seller-search", name: 'coin-seller-search',
component: () => import("../views/CoinSellerSearchView.vue"), component: () => import('../views/CoinSellerSearchView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/seller-records", path: '/seller-records',
name: "seller-records", name: 'seller-records',
component: () => import("../views/SellerRecordsView.vue"), component: () => import('../views/SellerRecordsView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/bd-setting", path: '/bd-setting',
name: "bd-setting", name: 'bd-setting',
component: () => import("../views/BDSettingView.vue"), component: () => import('../views/BDSettingView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
// 公共页面 // 公共页面
{ {
path: "/not_app", path: '/not_app',
name: "not-app", name: 'not-app',
component: () => import("../views/NotAppView.vue"), component: () => import('../views/NotAppView.vue'),
meta: { requiresAuth: false }, meta: { requiresAuth: false },
}, },
// 404 页面 // 404 页面
{ {
path: "/:pathMatch(.*)*", path: '/:pathMatch(.*)*',
name: "not-found", name: 'not-found',
redirect: "/apply", redirect: '/apply',
}, },
{ {
path: "/history-salary", path: '/history-salary',
name: "history-salary", name: 'history-salary',
component: () => import("../views/HistorySalaryView.vue"), component: () => import('../views/HistorySalaryView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/platform-salary", path: '/platform-salary',
name: "platform-salary", name: 'platform-salary',
component: () => import("../views/PlatformPolicyView.vue"), component: () => import('../views/PlatformPolicyView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/admin-center", path: '/admin-center',
name: "admin-center", name: 'admin-center',
component: () => import("../views/AdminCenter.vue"), component: () => import('../views/AdminCenter.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/item-distribution", path: '/item-distribution',
name: "item-distribution", name: 'item-distribution',
component: () => import("../views/ItemDistribution.vue"), component: () => import('../views/ItemDistribution.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/recharge", path: '/recharge',
name: "recharge", name: 'recharge',
component: () => import("../views/Recharge.vue"), component: () => import('../views/Recharge.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/recharge-standard", path: '/recharge-standard',
name: "recharge-standard", name: 'recharge-standard',
component: () => import("../views/RechargeStandard.vue"), component: () => import('../views/RechargeStandard.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/recharge-freight-agent", path: '/recharge-freight-agent',
name: "recharge-freight-agent", name: 'recharge-freight-agent',
component: () => import("../views/RechargeFreightAgent.vue"), component: () => import('../views/RechargeFreightAgent.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/map", path: '/map',
name: "map", name: 'map',
component: () => import("../views/map.vue"), component: () => import('../views/map.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{ {
path: "/pay-result", path: '/pay-result',
name: "pay-result", name: 'pay-result',
component: () => import("../views/payResult.vue"), component: () => import('../views/payResult.vue'),
meta: { requiresAuth: true },
},
{
path: '/top-list',
name: 'top-list',
component: () => import('../views/TopList/TopList.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
], ],
}); })
// 安装路由守卫 // 安装路由守卫
installRouteGuard(router); installRouteGuard(router)
export default router; export default router

View File

@ -5,33 +5,33 @@
// 用户身份类型 // 用户身份类型
export const USER_ROLES = { export const USER_ROLES = {
FREIGHT_AGENT: "freightAgent", FREIGHT_AGENT: 'freightAgent',
AGENT: "agent", AGENT: 'agent',
BD: "bd", BD: 'bd',
ANCHOR: "anchor", ANCHOR: 'anchor',
GUEST: "guest", GUEST: 'guest',
ADMIN: "admin", ADMIN: 'admin',
}; }
// 页面路径常量 // 页面路径常量
export const PAGES = { export const PAGES = {
COIN_SELLER: "/coin-seller", COIN_SELLER: '/coin-seller',
AGENCY_CENTER: "/agency-center", AGENCY_CENTER: '/agency-center',
BD_CENTER: "/bd-center", BD_CENTER: '/bd-center',
HOST_CENTER: "/host-center", HOST_CENTER: '/host-center',
APPLY: "/apply", APPLY: '/apply',
MESSAGE: "/message", MESSAGE: '/message',
TRANSFER: "/transfer", TRANSFER: '/transfer',
EXCHANGE: "/exchange-gold-coins", EXCHANGE: '/exchange-gold-coins',
PERSONAL_SALARY: "/platform-policy", PERSONAL_SALARY: '/platform-policy',
HOST_SETTING: "/host-setting", HOST_SETTING: '/host-setting',
NOT_APP: "/not_app", NOT_APP: '/not_app',
LOADING: "/loading", LOADING: '/loading',
ADMIN_CENTER: "/admin-center", ADMIN_CENTER: '/admin-center',
RECHARGE_STANDARD: "/recharge-standard", RECHARGE_STANDARD: '/recharge-standard',
PAY_RESULT: "/pay-result", PAY_RESULT: '/pay-result',
MAP: "/map", MAP: '/map',
}; }
// 🎯 核心改变:基于身份的权限配置 // 🎯 核心改变:基于身份的权限配置
// 每个身份拥有的页面权限列表 // 每个身份拥有的页面权限列表
@ -39,9 +39,9 @@ export const ROLE_PERMISSIONS = {
// Freight Agent (货运代理/金币销售员) // Freight Agent (货运代理/金币销售员)
[USER_ROLES.FREIGHT_AGENT]: [ [USER_ROLES.FREIGHT_AGENT]: [
PAGES.COIN_SELLER, // 主页面 PAGES.COIN_SELLER, // 主页面
"/seller-records", '/seller-records',
"/coin-seller-search", '/coin-seller-search',
"/recharge-freight-agent", '/recharge-freight-agent',
], ],
// Agent (代理商) // Agent (代理商)
@ -52,13 +52,13 @@ export const ROLE_PERMISSIONS = {
PAGES.MESSAGE, // 消息功能 PAGES.MESSAGE, // 消息功能
PAGES.PERSONAL_SALARY, // 个人薪资查看 PAGES.PERSONAL_SALARY, // 个人薪资查看
PAGES.RECHARGE_STANDARD, //普通转账功能 PAGES.RECHARGE_STANDARD, //普通转账功能
"/team-member", '/team-member',
"/information-details", '/information-details',
"/search-payee", '/search-payee',
"/history-salary", '/history-salary',
"/platform-salary", '/platform-salary',
"/invite-members", '/invite-members',
"/team-bill", '/team-bill',
], ],
// BD (商务拓展) // BD (商务拓展)
@ -68,9 +68,9 @@ export const ROLE_PERMISSIONS = {
PAGES.EXCHANGE, // 兑换功能 PAGES.EXCHANGE, // 兑换功能
PAGES.MESSAGE, // 消息功能 PAGES.MESSAGE, // 消息功能
PAGES.RECHARGE_STANDARD, //普通转账功能 PAGES.RECHARGE_STANDARD, //普通转账功能
"/team-member", '/team-member',
"/history-salary", '/history-salary',
"/platform-salary", '/platform-salary',
], ],
// Anchor (主播) // Anchor (主播)
@ -82,19 +82,19 @@ export const ROLE_PERMISSIONS = {
PAGES.PERSONAL_SALARY, // 个人薪资查看 PAGES.PERSONAL_SALARY, // 个人薪资查看
PAGES.HOST_SETTING, // 主播设置 PAGES.HOST_SETTING, // 主播设置
PAGES.RECHARGE_STANDARD, //普通转账功能 PAGES.RECHARGE_STANDARD, //普通转账功能
"/information-details", '/information-details',
"/search-payee", '/search-payee',
"/apply", '/apply',
"/history-salary", '/history-salary',
"/platform-salary", '/platform-salary',
"/invite-members", '/invite-members',
"/team-bill", '/team-bill',
], ],
// Admin管理员 // Admin管理员
[USER_ROLES.ADMIN]: [ [USER_ROLES.ADMIN]: [
PAGES.ADMIN_CENTER, //管理员中心 PAGES.ADMIN_CENTER, //管理员中心
"/item-distribution", //赠送商品页面 '/item-distribution', //赠送商品页面
PAGES.RECHARGE_STANDARD, //普通转账功能 PAGES.RECHARGE_STANDARD, //普通转账功能
], ],
@ -108,13 +108,14 @@ export const ROLE_PERMISSIONS = {
PAGES.NOT_APP, // 错误页面 PAGES.NOT_APP, // 错误页面
PAGES.PAY_RESULT, // 支付成功页面 PAGES.PAY_RESULT, // 支付成功页面
PAGES.MAP, // 地图选择页面 PAGES.MAP, // 地图选择页面
"/recharge", '/recharge',
"/recharge-freight-agent", '/recharge-freight-agent',
'/top-list',
], ],
// 加载页面 // 加载页面
LOADING_PAGE: [PAGES.LOADING], LOADING_PAGE: [PAGES.LOADING],
}; }
// 身份优先级(数字越大优先级越高) // 身份优先级(数字越大优先级越高)
export const ROLE_PRIORITY = { export const ROLE_PRIORITY = {
@ -124,7 +125,7 @@ export const ROLE_PRIORITY = {
[USER_ROLES.AGENT]: 2, [USER_ROLES.AGENT]: 2,
[USER_ROLES.ANCHOR]: 1, [USER_ROLES.ANCHOR]: 1,
[USER_ROLES.GUEST]: 0, [USER_ROLES.GUEST]: 0,
}; }
// 身份对应的默认首页 // 身份对应的默认首页
export const ROLE_DEFAULT_PAGES = { export const ROLE_DEFAULT_PAGES = {
@ -134,52 +135,52 @@ export const ROLE_DEFAULT_PAGES = {
[USER_ROLES.BD]: PAGES.BD_CENTER, [USER_ROLES.BD]: PAGES.BD_CENTER,
[USER_ROLES.ANCHOR]: PAGES.HOST_CENTER, [USER_ROLES.ANCHOR]: PAGES.HOST_CENTER,
[USER_ROLES.GUEST]: PAGES.APPLY, [USER_ROLES.GUEST]: PAGES.APPLY,
}; }
// 身份显示配置 // 身份显示配置
export const ROLE_DISPLAY_CONFIG = { export const ROLE_DISPLAY_CONFIG = {
[USER_ROLES.FREIGHT_AGENT]: { [USER_ROLES.FREIGHT_AGENT]: {
name: "Freight Agent", name: 'Freight Agent',
tag: "💰 Coin Seller", tag: '💰 Coin Seller',
color: "#FCD34D", color: '#FCD34D',
bgColor: "#FEF3C7", bgColor: '#FEF3C7',
}, },
[USER_ROLES.AGENT]: { [USER_ROLES.AGENT]: {
name: "Agent", name: 'Agent',
tag: "🏢 Agency", tag: '🏢 Agency',
color: "#1E40AF", color: '#1E40AF',
bgColor: "#DBEAFE", bgColor: '#DBEAFE',
}, },
[USER_ROLES.BD]: { [USER_ROLES.BD]: {
name: "BD", name: 'BD',
tag: "📈 BD", tag: '📈 BD',
color: "#059669", color: '#059669',
bgColor: "#D1FAE5", bgColor: '#D1FAE5',
}, },
[USER_ROLES.ANCHOR]: { [USER_ROLES.ANCHOR]: {
name: "Anchor", name: 'Anchor',
tag: "👑 Host", tag: '👑 Host',
color: "#5B21B6", color: '#5B21B6',
bgColor: "#E0E7FF", bgColor: '#E0E7FF',
}, },
[USER_ROLES.GUEST]: { [USER_ROLES.GUEST]: {
name: "Guest", name: 'Guest',
tag: "👤 Guest", tag: '👤 Guest',
color: "#6B7280", color: '#6B7280',
bgColor: "#F3F4F6", bgColor: '#F3F4F6',
}, },
}; }
/** /**
* 基于身份的权限管理类 * 基于身份的权限管理类
*/ */
class RoleBasedPermissionManager { class RoleBasedPermissionManager {
constructor() { constructor() {
this.userIdentity = null; this.userIdentity = null
this.primaryRole = null; this.primaryRole = null
this.allRoles = []; this.allRoles = []
this.isIdentityLoaded = false; this.isIdentityLoaded = false
this.allowedPages = []; this.allowedPages = []
} }
/** /**
@ -187,18 +188,18 @@ class RoleBasedPermissionManager {
* @param {Object} identity - 身份信息对象 * @param {Object} identity - 身份信息对象
*/ */
setUserIdentity(identity) { setUserIdentity(identity) {
this.userIdentity = identity; this.userIdentity = identity
this.allRoles = this.calculateAllRoles(identity); this.allRoles = this.calculateAllRoles(identity)
this.primaryRole = this.calculatePrimaryRole(this.allRoles); this.primaryRole = this.calculatePrimaryRole(this.allRoles)
this.allowedPages = this.calculateAllowedPages(this.allRoles); this.allowedPages = this.calculateAllowedPages(this.allRoles)
this.isIdentityLoaded = true; this.isIdentityLoaded = true
console.debug("🔐 User identity updated:", { console.debug('🔐 User identity updated:', {
identity: this.userIdentity, identity: this.userIdentity,
allRoles: this.allRoles, allRoles: this.allRoles,
primaryRole: this.primaryRole, primaryRole: this.primaryRole,
allowedPages: this.allowedPages, allowedPages: this.allowedPages,
}); })
} }
/** /**
@ -207,16 +208,16 @@ class RoleBasedPermissionManager {
* @returns {Array} 所有身份列表 * @returns {Array} 所有身份列表
*/ */
calculateAllRoles(identity) { calculateAllRoles(identity) {
if (!identity) return [USER_ROLES.GUEST]; if (!identity) return [USER_ROLES.GUEST]
const roles = []; const roles = []
if (identity.freightAgent) roles.push(USER_ROLES.FREIGHT_AGENT); if (identity.freightAgent) roles.push(USER_ROLES.FREIGHT_AGENT)
if (identity.agent) roles.push(USER_ROLES.AGENT); if (identity.agent) roles.push(USER_ROLES.AGENT)
if (identity.bd) roles.push(USER_ROLES.BD); if (identity.bd) roles.push(USER_ROLES.BD)
if (identity.anchor) roles.push(USER_ROLES.ANCHOR); if (identity.anchor) roles.push(USER_ROLES.ANCHOR)
if (identity.admin) roles.push(USER_ROLES.ADMIN); if (identity.admin) roles.push(USER_ROLES.ADMIN)
return roles.length > 0 ? roles : [USER_ROLES.GUEST]; return roles.length > 0 ? roles : [USER_ROLES.GUEST]
} }
/** /**
@ -225,12 +226,12 @@ class RoleBasedPermissionManager {
* @returns {string} 主要身份 * @returns {string} 主要身份
*/ */
calculatePrimaryRole(roles) { calculatePrimaryRole(roles) {
if (!roles || roles.length === 0) return USER_ROLES.GUEST; if (!roles || roles.length === 0) return USER_ROLES.GUEST
// 按优先级排序,返回优先级最高的身份 // 按优先级排序,返回优先级最高的身份
return roles.reduce((highest, current) => { return roles.reduce((highest, current) => {
return ROLE_PRIORITY[current] > ROLE_PRIORITY[highest] ? current : highest; return ROLE_PRIORITY[current] > ROLE_PRIORITY[highest] ? current : highest
}, USER_ROLES.GUEST); }, USER_ROLES.GUEST)
} }
/** /**
@ -239,27 +240,27 @@ class RoleBasedPermissionManager {
* @returns {Array} 允许访问的页面列表 * @returns {Array} 允许访问的页面列表
*/ */
calculateAllowedPages(roles) { calculateAllowedPages(roles) {
const allowedPages = new Set(); const allowedPages = new Set()
// 添加公共页面 // 添加公共页面
ROLE_PERMISSIONS.PUBLIC_PAGES.forEach((page) => { ROLE_PERMISSIONS.PUBLIC_PAGES.forEach((page) => {
allowedPages.add(page); allowedPages.add(page)
}); })
// 添加loading页面 // 添加loading页面
ROLE_PERMISSIONS.LOADING_PAGE.forEach((page) => { ROLE_PERMISSIONS.LOADING_PAGE.forEach((page) => {
allowedPages.add(page); allowedPages.add(page)
}); })
// 根据用户拥有的每个身份,添加对应的页面权限 // 根据用户拥有的每个身份,添加对应的页面权限
roles.forEach((role) => { roles.forEach((role) => {
const rolePages = ROLE_PERMISSIONS[role] || []; const rolePages = ROLE_PERMISSIONS[role] || []
rolePages.forEach((page) => { rolePages.forEach((page) => {
allowedPages.add(page); allowedPages.add(page)
}); })
}); })
return Array.from(allowedPages); return Array.from(allowedPages)
} }
/** /**
@ -269,20 +270,20 @@ class RoleBasedPermissionManager {
*/ */
hasPagePermission(page) { hasPagePermission(page) {
if (!this.isIdentityLoaded) { if (!this.isIdentityLoaded) {
console.warn("⚠️ Identity not loaded yet"); console.warn('⚠️ Identity not loaded yet')
return false; return false
} }
const hasPermission = this.allowedPages.includes(page); const hasPermission = this.allowedPages.includes(page)
console.debug("🔍 Permission check:", { console.debug('🔍 Permission check:', {
page, page,
allRoles: this.allRoles, allRoles: this.allRoles,
primaryRole: this.primaryRole, primaryRole: this.primaryRole,
hasPermission, hasPermission,
}); })
return hasPermission; return hasPermission
} }
/** /**
@ -292,10 +293,10 @@ class RoleBasedPermissionManager {
* @returns {boolean} 是否有权限 * @returns {boolean} 是否有权限
*/ */
hasRolePagePermission(role, page) { hasRolePagePermission(role, page) {
const rolePages = ROLE_PERMISSIONS[role] || []; const rolePages = ROLE_PERMISSIONS[role] || []
const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || []; const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || []
return rolePages.includes(page) || publicPages.includes(page); return rolePages.includes(page) || publicPages.includes(page)
} }
/** /**
@ -304,10 +305,10 @@ class RoleBasedPermissionManager {
* @returns {Array} 页面权限列表 * @returns {Array} 页面权限列表
*/ */
getRolePermissions(role) { getRolePermissions(role) {
const rolePages = ROLE_PERMISSIONS[role] || []; const rolePages = ROLE_PERMISSIONS[role] || []
const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || []; const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || []
return [...new Set([...rolePages, ...publicPages])]; return [...new Set([...rolePages, ...publicPages])]
} }
/** /**
@ -315,7 +316,7 @@ class RoleBasedPermissionManager {
* @returns {string} 默认页面路径 * @returns {string} 默认页面路径
*/ */
getDefaultPage() { getDefaultPage() {
return ROLE_DEFAULT_PAGES[this.primaryRole] || PAGES.APPLY; return ROLE_DEFAULT_PAGES[this.primaryRole] || PAGES.APPLY
} }
/** /**
@ -323,7 +324,7 @@ class RoleBasedPermissionManager {
* @returns {string} 当前主要身份 * @returns {string} 当前主要身份
*/ */
getPrimaryRole() { getPrimaryRole() {
return this.primaryRole; return this.primaryRole
} }
/** /**
@ -331,7 +332,7 @@ class RoleBasedPermissionManager {
* @returns {Array} 所有身份 * @returns {Array} 所有身份
*/ */
getAllRoles() { getAllRoles() {
return this.allRoles; return this.allRoles
} }
/** /**
@ -340,7 +341,7 @@ class RoleBasedPermissionManager {
* @returns {Object} 显示信息 * @returns {Object} 显示信息
*/ */
getRoleDisplayInfo(role = this.primaryRole) { getRoleDisplayInfo(role = this.primaryRole) {
return ROLE_DISPLAY_CONFIG[role] || ROLE_DISPLAY_CONFIG[USER_ROLES.GUEST]; return ROLE_DISPLAY_CONFIG[role] || ROLE_DISPLAY_CONFIG[USER_ROLES.GUEST]
} }
/** /**
@ -348,7 +349,7 @@ class RoleBasedPermissionManager {
* @returns {boolean} 是否有多重身份 * @returns {boolean} 是否有多重身份
*/ */
hasMultipleRoles() { hasMultipleRoles() {
return this.allRoles.length > 1; return this.allRoles.length > 1
} }
/** /**
@ -358,11 +359,11 @@ class RoleBasedPermissionManager {
*/ */
switchToRole(role) { switchToRole(role) {
if (!this.allRoles.includes(role)) { if (!this.allRoles.includes(role)) {
console.warn(`⚠️ User does not have role: ${role}`); console.warn(`⚠️ User does not have role: ${role}`)
return this.getDefaultPage(); return this.getDefaultPage()
} }
return ROLE_DEFAULT_PAGES[role] || PAGES.APPLY; return ROLE_DEFAULT_PAGES[role] || PAGES.APPLY
} }
/** /**
@ -370,16 +371,16 @@ class RoleBasedPermissionManager {
* @returns {Array} 可访问的页面菜单 * @returns {Array} 可访问的页面菜单
*/ */
getNavigationMenu() { getNavigationMenu() {
const menu = []; const menu = []
this.allowedPages.forEach((page) => { this.allowedPages.forEach((page) => {
const menuItem = this.getPageMenuInfo(page); const menuItem = this.getPageMenuInfo(page)
if (menuItem) { if (menuItem) {
menu.push(menuItem); menu.push(menuItem)
} }
}); })
return menu; return menu
} }
/** /**
@ -389,31 +390,31 @@ class RoleBasedPermissionManager {
*/ */
getPageMenuInfo(page) { getPageMenuInfo(page) {
const pageMenuConfig = { const pageMenuConfig = {
[PAGES.COIN_SELLER]: { title: "Coin Seller", icon: "💰", order: 1 }, [PAGES.COIN_SELLER]: { title: 'Coin Seller', icon: '💰', order: 1 },
[PAGES.AGENCY_CENTER]: { title: "Agency Center", icon: "🏢", order: 2 }, [PAGES.AGENCY_CENTER]: { title: 'Agency Center', icon: '🏢', order: 2 },
[PAGES.BD_CENTER]: { title: "BD Center", icon: "📈", order: 3 }, [PAGES.BD_CENTER]: { title: 'BD Center', icon: '📈', order: 3 },
[PAGES.HOST_CENTER]: { title: "Host Center", icon: "👑", order: 4 }, [PAGES.HOST_CENTER]: { title: 'Host Center', icon: '👑', order: 4 },
[PAGES.TRANSFER]: { title: "Transfer", icon: "💸", order: 5 }, [PAGES.TRANSFER]: { title: 'Transfer', icon: '💸', order: 5 },
[PAGES.EXCHANGE]: { title: "Exchange", icon: "🔄", order: 6 }, [PAGES.EXCHANGE]: { title: 'Exchange', icon: '🔄', order: 6 },
[PAGES.MESSAGE]: { title: "Messages", icon: "💬", order: 7 }, [PAGES.MESSAGE]: { title: 'Messages', icon: '💬', order: 7 },
[PAGES.PERSONAL_SALARY]: { title: "Salary", icon: "💵", order: 8 }, [PAGES.PERSONAL_SALARY]: { title: 'Salary', icon: '💵', order: 8 },
[PAGES.HOST_SETTING]: { title: "Settings", icon: "⚙️", order: 9 }, [PAGES.HOST_SETTING]: { title: 'Settings', icon: '⚙️', order: 9 },
}; }
const config = pageMenuConfig[page]; const config = pageMenuConfig[page]
return config ? { ...config, path: page } : null; return config ? { ...config, path: page } : null
} }
/** /**
* 重置权限信息 * 重置权限信息
*/ */
reset() { reset() {
this.userIdentity = null; this.userIdentity = null
this.primaryRole = null; this.primaryRole = null
this.allRoles = []; this.allRoles = []
this.isIdentityLoaded = false; this.isIdentityLoaded = false
this.allowedPages = []; this.allowedPages = []
console.debug("🔄 Permission manager reset"); console.debug('🔄 Permission manager reset')
} }
/** /**
@ -421,41 +422,40 @@ class RoleBasedPermissionManager {
* @returns {boolean} * @returns {boolean}
*/ */
isLoaded() { isLoaded() {
return this.isIdentityLoaded; return this.isIdentityLoaded
} }
/** /**
* 调试方法打印当前权限状态 * 调试方法打印当前权限状态
*/ */
debugPermissions() { debugPermissions() {
console.group("🔐 Current Permission State"); console.group('🔐 Current Permission State')
console.log("User Identity:", this.userIdentity); console.log('User Identity:', this.userIdentity)
console.log("All Roles:", this.allRoles); console.log('All Roles:', this.allRoles)
console.log("Primary Role:", this.primaryRole); console.log('Primary Role:', this.primaryRole)
console.log("Allowed Pages:", this.allowedPages); console.log('Allowed Pages:', this.allowedPages)
console.log("Default Page:", this.getDefaultPage()); console.log('Default Page:', this.getDefaultPage())
console.log("Navigation Menu:", this.getNavigationMenu()); console.log('Navigation Menu:', this.getNavigationMenu())
console.groupEnd(); console.groupEnd()
} }
} }
// 创建全局权限管理实例 // 创建全局权限管理实例
export const permissionManager = new RoleBasedPermissionManager(); export const permissionManager = new RoleBasedPermissionManager()
// 便捷方法 // 便捷方法
export const hasPermission = (page) => permissionManager.hasPagePermission(page); export const hasPermission = (page) => permissionManager.hasPagePermission(page)
export const hasRolePermission = (role, page) => export const hasRolePermission = (role, page) => permissionManager.hasRolePagePermission(role, page)
permissionManager.hasRolePagePermission(role, page); export const getRolePermissions = (role) => permissionManager.getRolePermissions(role)
export const getRolePermissions = (role) => permissionManager.getRolePermissions(role); export const getDefaultPage = () => permissionManager.getDefaultPage()
export const getDefaultPage = () => permissionManager.getDefaultPage(); export const getPrimaryRole = () => permissionManager.getPrimaryRole()
export const getPrimaryRole = () => permissionManager.getPrimaryRole(); export const getAllRoles = () => permissionManager.getAllRoles()
export const getAllRoles = () => permissionManager.getAllRoles(); export const getRoleDisplayInfo = (role) => permissionManager.getRoleDisplayInfo(role)
export const getRoleDisplayInfo = (role) => permissionManager.getRoleDisplayInfo(role); export const setUserIdentity = (identity) => permissionManager.setUserIdentity(identity)
export const setUserIdentity = (identity) => permissionManager.setUserIdentity(identity); export const resetPermissions = () => permissionManager.reset()
export const resetPermissions = () => permissionManager.reset(); export const switchToRole = (role) => permissionManager.switchToRole(role)
export const switchToRole = (role) => permissionManager.switchToRole(role); export const getNavigationMenu = () => permissionManager.getNavigationMenu()
export const getNavigationMenu = () => permissionManager.getNavigationMenu(); export const debugPermissions = () => permissionManager.debugPermissions()
export const debugPermissions = () => permissionManager.debugPermissions();
// 🎯 新增:快速查询某个身份的权限 // 🎯 新增:快速查询某个身份的权限
export const QUICK_ROLE_CHECK = { export const QUICK_ROLE_CHECK = {
@ -482,4 +482,4 @@ export const QUICK_ROLE_CHECK = {
pages: getRolePermissions(USER_ROLES.ANCHOR), pages: getRolePermissions(USER_ROLES.ANCHOR),
canAccess: (page) => hasRolePermission(USER_ROLES.ANCHOR, page), canAccess: (page) => hasRolePermission(USER_ROLES.ANCHOR, page),
}, },
}; }

View File

@ -3,24 +3,24 @@
* 提供APP连接检查页面访问权限控制和自动重定向功能 * 提供APP连接检查页面访问权限控制和自动重定向功能
*/ */
import { permissionManager, PAGES, USER_ROLES } from "./permissionManager.js"; import { permissionManager, PAGES, USER_ROLES } from './permissionManager.js'
import { getUserIdentity, getMemberProfile } from "../api/wallet.js"; import { getUserIdentity, getMemberProfile } from '../api/wallet.js'
import { getUserId, setUserInfo } from "./userStore.js"; import { getUserId, setUserInfo } from './userStore.js'
import { import {
connectApplication, connectApplication,
parseAccessOrigin, parseAccessOrigin,
parseHeader, parseHeader,
setHttpHeaders, setHttpHeaders,
isInApp, isInApp,
} from "./appBridge.js"; } from './appBridge.js'
import { appConnectionManager, isAppConnected } from "./appConnectionManager.js"; import { appConnectionManager, isAppConnected } from './appConnectionManager.js'
/** /**
* APP连接检查器 * APP连接检查器
*/ */
class AppConnectionChecker { class AppConnectionChecker {
constructor() { constructor() {
this.isConnecting = false; this.isConnecting = false
} }
/** /**
@ -28,38 +28,38 @@ class AppConnectionChecker {
* @returns {Promise<boolean>} 连接是否成功 * @returns {Promise<boolean>} 连接是否成功
*/ */
async ensureConnection() { async ensureConnection() {
console.debug("🔗 Checking APP connection..."); console.debug('🔗 Checking APP connection...')
// 如果不在APP环境中直接返回成功 // 如果不在APP环境中直接返回成功
console.log("我是app环境", window.app); console.log('我是app环境', window.app)
console.log("我是app环境", window.webkit); console.log('我是app环境', window.webkit)
console.log("我是app环境", window.FlutterPageControl); console.log('我是app环境', window.FlutterPageControl)
if (!isInApp()) { if (!isInApp()) {
console.debug("🌐 Browser environment, no connection needed"); console.debug('🌐 Browser environment, no connection needed')
return true; return true
} }
// 如果已经连接且未超时,直接返回成功 // 如果已经连接且未超时,直接返回成功
if (isAppConnected()) { if (isAppConnected()) {
console.debug("✅ APP already connected"); console.debug('✅ APP already connected')
return true; return true
} }
// 如果正在连接中,等待现有连接 // 如果正在连接中,等待现有连接
if (appConnectionManager.isConnecting()) { if (appConnectionManager.isConnecting()) {
console.debug("⏳ Connection already in progress, waiting..."); console.debug('⏳ Connection already in progress, waiting...')
try { try {
await appConnectionManager.getConnectionPromise(); await appConnectionManager.getConnectionPromise()
return isAppConnected(); return isAppConnected()
} catch (error) { } catch (error) {
console.error("❌ Connection wait failed:", error); console.error('❌ Connection wait failed:', error)
return false; return false
} }
} }
// 开始新的连接 // 开始新的连接
return await this._performConnection(); return await this._performConnection()
} }
/** /**
@ -68,51 +68,51 @@ class AppConnectionChecker {
*/ */
async _performConnection() { async _performConnection() {
try { try {
console.debug("📱 Starting new APP connection..."); console.debug('📱 Starting new APP connection...')
const connectionPromise = new Promise((resolve, reject) => { const connectionPromise = new Promise((resolve, reject) => {
connectApplication(async (access) => { connectApplication(async (access) => {
try { try {
const result = parseAccessOrigin(access); const result = parseAccessOrigin(access)
if (result.success) { if (result.success) {
// 解析头部信息 // 解析头部信息
const headerInfo = parseHeader(result.data); const headerInfo = parseHeader(result.data)
// 设置HTTP请求头 // 设置HTTP请求头
await setHttpHeaders(headerInfo); await setHttpHeaders(headerInfo)
// 更新连接状态 // 更新连接状态
appConnectionManager.setConnected(headerInfo); appConnectionManager.setConnected(headerInfo)
console.debug("🎉 APP connection established successfully"); console.debug('🎉 APP connection established successfully')
// APP连接成功后立即获取用户信息 // APP连接成功后立即获取用户信息
await this._fetchUserInfoAfterConnection(); await this._fetchUserInfoAfterConnection()
resolve(true); resolve(true)
} else { } else {
console.error("❌ Failed to parse access origin:", result.error); console.error('❌ Failed to parse access origin:', result.error)
appConnectionManager.reset(); appConnectionManager.reset()
reject(new Error(result.error)); reject(new Error(result.error))
} }
} catch (error) { } catch (error) {
console.error("❌ Connection process failed:", error); console.error('❌ Connection process failed:', error)
appConnectionManager.reset(); appConnectionManager.reset()
reject(error); reject(error)
} }
}); })
}); })
// 设置连接状态 // 设置连接状态
appConnectionManager.setConnecting(connectionPromise); appConnectionManager.setConnecting(connectionPromise)
// 等待连接完成 // 等待连接完成
return await connectionPromise; return await connectionPromise
} catch (error) { } catch (error) {
console.error("❌ APP connection failed:", error); console.error('❌ APP connection failed:', error)
appConnectionManager.reset(); appConnectionManager.reset()
return false; return false
} }
} }
@ -122,21 +122,21 @@ class AppConnectionChecker {
*/ */
async _fetchUserInfoAfterConnection() { async _fetchUserInfoAfterConnection() {
try { try {
console.debug("👤 Fetching user info after connection..."); console.debug('👤 Fetching user info after connection...')
const response = await getMemberProfile(); const response = await getMemberProfile()
if (response && response.status && response.body) { if (response && response.status && response.body) {
// 缓存用户信息 // 缓存用户信息
console.debug("✅ User info cached successfully"); console.debug('✅ User info cached successfully')
setUserInfo(response.body, null); setUserInfo(response.body, null)
} else { } else {
console.warn("⚠️ Failed to get team entry or invalid response"); console.warn('⚠️ Failed to get team entry or invalid response')
} }
} catch (error) { } catch (error) {
// 这里不抛出错误因为APP连接已经成功 // 这里不抛出错误因为APP连接已经成功
// 用户信息获取失败可以在后续的身份检查中处理 // 用户信息获取失败可以在后续的身份检查中处理
console.warn("⚠️ Failed to fetch user info after connection:", error); console.warn('⚠️ Failed to fetch user info after connection:', error)
} }
} }
@ -144,7 +144,7 @@ class AppConnectionChecker {
* 重置连接状态 * 重置连接状态
*/ */
reset() { reset() {
appConnectionManager.reset(); appConnectionManager.reset()
} }
} }
@ -153,8 +153,8 @@ class AppConnectionChecker {
*/ */
class IdentityChecker { class IdentityChecker {
constructor() { constructor() {
this.isChecking = false; this.isChecking = false
this.checkPromise = null; this.checkPromise = null
} }
/** /**
@ -164,26 +164,26 @@ class IdentityChecker {
*/ */
async checkUserIdentity(userId) { async checkUserIdentity(userId) {
if (!userId) { if (!userId) {
console.warn("⚠️ No user ID provided"); console.warn('⚠️ No user ID provided')
permissionManager.setUserIdentity(null); permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST; return USER_ROLES.GUEST
} }
// 如果正在检查中,等待现有的检查完成 // 如果正在检查中,等待现有的检查完成
if (this.isChecking && this.checkPromise) { if (this.isChecking && this.checkPromise) {
console.debug("⏳ Identity check already in progress, waiting..."); console.debug('⏳ Identity check already in progress, waiting...')
return await this.checkPromise; return await this.checkPromise
} }
this.isChecking = true; this.isChecking = true
this.checkPromise = this._performIdentityCheck(userId); this.checkPromise = this._performIdentityCheck(userId)
try { try {
const primaryRole = await this.checkPromise; const primaryRole = await this.checkPromise
return primaryRole; return primaryRole
} finally { } finally {
this.isChecking = false; this.isChecking = false
this.checkPromise = null; this.checkPromise = null
} }
} }
@ -194,27 +194,27 @@ class IdentityChecker {
*/ */
async _performIdentityCheck(userId) { async _performIdentityCheck(userId) {
try { try {
console.debug("🔍 Checking user identity for:", userId); console.debug('🔍 Checking user identity for:', userId)
const response = await getUserIdentity(); const response = await getUserIdentity()
if (response && response.status && response.body) { if (response && response.status && response.body) {
const identity = response.body; const identity = response.body
console.debug("✅ Identity fetched:", identity); console.debug('✅ Identity fetched:', identity)
// 设置权限信息 // 设置权限信息
permissionManager.setUserIdentity(identity); permissionManager.setUserIdentity(identity)
return permissionManager.getPrimaryRole(); return permissionManager.getPrimaryRole()
} else { } else {
console.warn("⚠️ Invalid identity response"); console.warn('⚠️ Invalid identity response')
permissionManager.setUserIdentity(null); permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST; return USER_ROLES.GUEST
} }
} catch (error) { } catch (error) {
console.error("❌ Failed to fetch user identity:", error); console.error('❌ Failed to fetch user identity:', error)
permissionManager.setUserIdentity(null); permissionManager.setUserIdentity(null)
return USER_ROLES.GUEST; return USER_ROLES.GUEST
} }
} }
@ -222,8 +222,8 @@ class IdentityChecker {
* 重置检查状态 * 重置检查状态
*/ */
reset() { reset() {
this.isChecking = false; this.isChecking = false
this.checkPromise = null; this.checkPromise = null
} }
} }
@ -239,36 +239,36 @@ class PageRedirector {
* @returns {Promise<boolean>} 是否进行了重定向 * @returns {Promise<boolean>} 是否进行了重定向
*/ */
async redirectToCorrectPage(router, targetPath, currentPath) { async redirectToCorrectPage(router, targetPath, currentPath) {
const primaryRole = permissionManager.getPrimaryRole(); const primaryRole = permissionManager.getPrimaryRole()
// 检查目标页面权限 // 检查目标页面权限
const hasPermission = permissionManager.hasPagePermission(targetPath); const hasPermission = permissionManager.hasPagePermission(targetPath)
console.debug("🔄 Redirect check:", { console.debug('🔄 Redirect check:', {
targetPath, targetPath,
currentPath, currentPath,
primaryRole, primaryRole,
hasPermission, hasPermission,
}); })
if (hasPermission) { if (hasPermission) {
// 有权限访问目标页面 // 有权限访问目标页面
if (targetPath !== currentPath) { if (targetPath !== currentPath) {
console.debug("✅ Access granted, navigating to:", targetPath); console.debug('✅ Access granted, navigating to:', targetPath)
return false; // 不需要重定向,让路由正常进行 return false // 不需要重定向,让路由正常进行
} }
return false; return false
} else { } else {
// 无权限访问,重定向到默认页面 // 无权限访问,重定向到默认页面
const defaultPage = permissionManager.getDefaultPage(); const defaultPage = permissionManager.getDefaultPage()
console.debug("❌ Access denied, redirecting to:", defaultPage); console.debug('❌ Access denied, redirecting to:', defaultPage)
if (defaultPage !== currentPath) { if (defaultPage !== currentPath) {
await router.replace(defaultPage); await router.replace(defaultPage)
return true; // 已进行重定向 return true // 已进行重定向
} }
return false; return false
} }
} }
@ -278,17 +278,17 @@ class PageRedirector {
* @param {string} attemptedPage - 尝试访问的页面 * @param {string} attemptedPage - 尝试访问的页面
*/ */
async handleUnauthorizedAccess(router, attemptedPage) { async handleUnauthorizedAccess(router, attemptedPage) {
const primaryRole = permissionManager.getPrimaryRole(); const primaryRole = permissionManager.getPrimaryRole()
const defaultPage = permissionManager.getDefaultPage(); const defaultPage = permissionManager.getDefaultPage()
console.warn("🚫 Unauthorized access attempt:", { console.warn('🚫 Unauthorized access attempt:', {
attemptedPage, attemptedPage,
primaryRole, primaryRole,
redirectingTo: defaultPage, redirectingTo: defaultPage,
}); })
// 重定向到用户有权限的默认页面 // 重定向到用户有权限的默认页面
await router.replace(defaultPage); await router.replace(defaultPage)
} }
} }
@ -297,10 +297,10 @@ class PageRedirector {
*/ */
class RouteGuard { class RouteGuard {
constructor() { constructor() {
this.appConnectionChecker = new AppConnectionChecker(); this.appConnectionChecker = new AppConnectionChecker()
this.identityChecker = new IdentityChecker(); this.identityChecker = new IdentityChecker()
this.pageRedirector = new PageRedirector(); this.pageRedirector = new PageRedirector()
this.isGuardActive = false; this.isGuardActive = false
} }
/** /**
@ -309,11 +309,11 @@ class RouteGuard {
*/ */
install(router) { install(router) {
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
await this.handleRouteChange(to, from, next); await this.handleRouteChange(to, from, next)
}); })
this.isGuardActive = true; this.isGuardActive = true
console.debug("🛡️ Route guard installed"); console.debug('🛡️ Route guard installed')
} }
/** /**
@ -323,68 +323,68 @@ class RouteGuard {
* @param {Function} next - 路由继续函数 * @param {Function} next - 路由继续函数
*/ */
async handleRouteChange(to, from, next) { async handleRouteChange(to, from, next) {
console.group("🛡️ Route Guard Check"); console.group('🛡️ Route Guard Check')
console.debug("📍 Route change:", from.path, "→", to.path); console.debug('📍 Route change:', from.path, '→', to.path)
try { try {
// 1. 检查是否为公共页面(无需任何验证) // 1. 检查是否为公共页面(无需任何验证)
if (this.isPublicPage(to.path)) { if (this.isPublicPage(to.path)) {
console.debug("🌐 Public page, allowing access"); console.debug('🌐 Public page, allowing access')
console.groupEnd(); console.groupEnd()
next(); next()
return; return
} }
// 2. 确保APP连接 // 2. 确保APP连接
console.debug("🔗 Ensuring APP connection..."); console.debug('🔗 Ensuring APP connection...')
const isConnected = await this.appConnectionChecker.ensureConnection(); const isConnected = await this.appConnectionChecker.ensureConnection()
if (!isConnected) { if (!isConnected) {
console.error("❌ APP connection failed, redirecting to error page"); console.error('❌ APP connection failed, redirecting to error page')
console.groupEnd(); console.groupEnd()
next(PAGES.NOT_APP); next(PAGES.NOT_APP)
return; return
} }
// 3. 检查用户身份是否已加载 // 3. 检查用户身份是否已加载
if (!permissionManager.isLoaded()) { if (!permissionManager.isLoaded()) {
console.debug("🔍 Identity not loaded, checking..."); console.debug('🔍 Identity not loaded, checking...')
const userId = getUserId(); const userId = getUserId()
if (!userId) { if (!userId) {
console.debug("❌ No user ID available, redirecting to apply"); console.debug('❌ No user ID available, redirecting to apply')
console.groupEnd(); console.groupEnd()
next(PAGES.APPLY); next(PAGES.APPLY)
return; return
} }
// 检查用户身份 // 检查用户身份
await this.identityChecker.checkUserIdentity(userId); await this.identityChecker.checkUserIdentity(userId)
} }
// 4. 检查页面访问权限 // 4. 检查页面访问权限
const hasPermission = permissionManager.hasPagePermission(to.path); const hasPermission = permissionManager.hasPagePermission(to.path)
if (hasPermission) { if (hasPermission) {
console.debug("✅ Permission granted, allowing access"); console.debug('✅ Permission granted, allowing access')
console.groupEnd(); console.groupEnd()
next(); next()
} else { } else {
console.debug("❌ Permission denied"); console.debug('❌ Permission denied')
const defaultPage = permissionManager.getDefaultPage(); const defaultPage = permissionManager.getDefaultPage()
console.debug("🔄 Redirecting to default page:", defaultPage); console.debug('🔄 Redirecting to default page:', defaultPage)
console.groupEnd(); console.groupEnd()
next(defaultPage); next(defaultPage)
} }
} catch (error) { } catch (error) {
console.error("❌ Route guard error:", error); console.error('❌ Route guard error:', error)
console.groupEnd(); console.groupEnd()
// 根据错误类型决定重定向目标 // 根据错误类型决定重定向目标
if (error.message && error.message.includes("connection")) { if (error.message && error.message.includes('connection')) {
next(PAGES.NOT_APP); // 连接相关错误 next(PAGES.NOT_APP) // 连接相关错误
} else { } else {
next(PAGES.APPLY); // 其他错误 next(PAGES.APPLY) // 其他错误
} }
} }
} }
@ -396,15 +396,19 @@ class RouteGuard {
*/ */
isPublicPage(path) { isPublicPage(path) {
const publicPages = [ const publicPages = [
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查 PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面 PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
'/404', // 404页面 '/404', // 404页面
'/error', // 通用错误页面 '/error', // 通用错误页面
'/recharge', '/recharge',
'/recharge-freight-agent', '/recharge-freight-agent',
'/pay-result' '/pay-result',
'/admin-center',
'/item-distribution',
'/map', //地图
] ]
return publicPages.includes(path); return publicPages.includes(path)
} }
/** /**
@ -414,26 +418,26 @@ class RouteGuard {
*/ */
async checkAndRedirect(router, userId) { async checkAndRedirect(router, userId) {
try { try {
console.debug("🔄 Manual identity check and redirect"); console.debug('🔄 Manual identity check and redirect')
// 检查身份 // 检查身份
const primaryRole = await this.identityChecker.checkUserIdentity(userId); const primaryRole = await this.identityChecker.checkUserIdentity(userId)
// 获取当前页面 // 获取当前页面
const currentPath = router.currentRoute.value.path; const currentPath = router.currentRoute.value.path
// 检查当前页面权限 // 检查当前页面权限
const hasPermission = permissionManager.hasPagePermission(currentPath); const hasPermission = permissionManager.hasPagePermission(currentPath)
if (!hasPermission) { if (!hasPermission) {
// 重定向到默认页面 // 重定向到默认页面
await this.pageRedirector.redirectToCorrectPage(router, currentPath, currentPath); await this.pageRedirector.redirectToCorrectPage(router, currentPath, currentPath)
} }
return primaryRole; return primaryRole
} catch (error) { } catch (error) {
console.error("❌ Manual check and redirect failed:", error); console.error('❌ Manual check and redirect failed:', error)
throw error; throw error
} }
} }
@ -441,17 +445,17 @@ class RouteGuard {
* 重置守卫状态 * 重置守卫状态
*/ */
reset() { reset() {
this.appConnectionChecker.reset(); this.appConnectionChecker.reset()
this.identityChecker.reset(); this.identityChecker.reset()
permissionManager.reset(); permissionManager.reset()
console.debug("🔄 Route guard reset"); console.debug('🔄 Route guard reset')
} }
} }
// 创建全局路由守卫实例 // 创建全局路由守卫实例
export const routeGuard = new RouteGuard(); export const routeGuard = new RouteGuard()
// 便捷方法 // 便捷方法
export const installRouteGuard = (router) => routeGuard.install(router); export const installRouteGuard = (router) => routeGuard.install(router)
export const checkAndRedirect = (router, userId) => routeGuard.checkAndRedirect(router, userId); export const checkAndRedirect = (router, userId) => routeGuard.checkAndRedirect(router, userId)
export const resetRouteGuard = () => routeGuard.reset(); export const resetRouteGuard = () => routeGuard.reset()

View File

@ -0,0 +1,215 @@
<template>
<div class="fullPage">
<div class="bg">
<!-- 状态栏占位区域仅在APP中显示 -->
<div v-if="isInAppEnvironment" style="height: 30px"></div>
<!-- 帮助按钮 -->
<img
src="/src/assets/images/TopList/help.png"
alt=""
style="width: 10%; position: absolute; top: 48px; right: 8px; z-index: 4"
/>
<!-- history按钮 -->
<img
src="/src/assets/images/TopList/history.png"
alt=""
style="width: 20%; margin-top: 20px"
/>
<div style="display: flex; flex-direction: column; align-items: center">
<!-- 主标题 -->
<img src="/src/assets/images/TopList/listTitle.png" alt="" style="width: 90%" />
<!-- 榜首 -->
<div style="width: 100%; display: flex; justify-content: space-around; margin-top: 16px">
<div style="width: 40%">
<div
style="
position: relative;
width: 100%;
min-height: max-content;
display: flex;
justify-content: center;
align-items: center;
"
>
<!-- 头像框 -->
<img
src="/src/assets/images/TopList/kingFrame.png"
alt=""
style="width: 100%; z-index: 1"
/>
<!-- 头像 -->
<div
style="
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 0;
"
>
<img src="/src/assets/icon/coin.png" alt="" style="width: 90%" />
</div>
<div
style="color: #fff; font-weight: 860; position: absolute; bottom: 8%; z-index: 2"
>
Sukabuliete
</div>
</div>
<div style="color: #fff; font-weight: 590; text-align: center">ID:123456</div>
</div>
<div style="width: 40%">
<div
style="
position: relative;
width: 100%;
min-height: max-content;
display: flex;
justify-content: center;
align-items: center;
"
>
<!-- 头像框 -->
<img
src="/src/assets/images/TopList/queenFrame.png"
alt=""
style="width: 100%; z-index: 1"
/>
<!-- 头像 -->
<div
style="
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 0;
"
>
<img src="/src/assets/icon/coin.png" alt="" style="width: 90%" />
</div>
<div
style="color: #fff; font-weight: 860; position: absolute; bottom: 5%; z-index: 2"
>
Sukabuliete
</div>
</div>
<div style="color: #fff; font-weight: 590; text-align: center">ID:123456</div>
</div>
</div>
</div>
<!-- rewards -->
<div style="display: flex; justify-content: flex-end">
<img src="/src/assets/images/TopList/rewards.png" alt="" style="width: 20%" />
</div>
<!-- 每周礼物 -->
<div style="display: flex; justify-content: center">
<div style="width: 100%; position: relative">
<img src="/src/assets/images/TopList/eventGifts.png" alt="" style="width: 100%" />
<div
style="
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="width: 80%; display: flex; justify-content: space-around; margin-top: 10%">
<div
v-for="(item, index) in 3"
:key="index"
style="width: 30%; display: flex; flex-direction: column"
>
<img style="" src="/src/assets/images/TopList/gift.png" alt="" />
<div style="color: rgba(255, 255, 255, 1); font-weight: 590; text-align: center">
ddd
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 排行榜 -->
<div style="width: 100vw">
<!-- 排行榜切换按钮 -->
<div style="width: 100%">
<div
v-if="visibleKingList"
style="width: 100%; display: flex; justify-content: space-around"
>
<img src="/src/assets/images/TopList/kingBtActive.png" alt="" style="width: 40%" />
<img
src="/src/assets/images/TopList/queenBt.png"
alt=""
style="width: 40%"
@click="visibleKingList = false"
/>
</div>
<div
v-if="!visibleKingList"
style="width: 100%; display: flex; justify-content: space-around"
>
<img
src="/src/assets/images/TopList/kingBt.png"
alt=""
style="width: 40%"
@click="visibleKingList = true"
/>
<img src="/src/assets/images/TopList/queenBtActive.png" alt="" style="width: 40%" />
</div>
</div>
<!-- 排行榜 -->
<div style="position: relative">
<img src="/src/assets/images/TopList/eventGifts.png" width="100%" alt="" style="" />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isInApp } from '../../utils/appBridge.js'
import { onMounted, ref } from 'vue'
const visibleKingList = ref(true)
// APP
const isInAppEnvironment = ref(false)
//
onMounted(() => {
isInAppEnvironment.value = isInApp()
})
</script>
<style scoped>
.fullPage {
background-color: rgba(77, 30, 22, 1);
min-height: 100vh;
}
.bg {
width: 100vw;
background-image: url(../../assets/images/TopList/bg.png);
background-size: 100% auto;
background-repeat: no-repeat;
}
</style>