feat(榜单): 新增榜单页面
BIN
src/assets/images/TopList/bg.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
src/assets/images/TopList/eventGifts.png
Normal file
|
After Width: | Height: | Size: 454 KiB |
BIN
src/assets/images/TopList/gift.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
src/assets/images/TopList/help.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/TopList/history.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/images/TopList/kingBt.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
src/assets/images/TopList/kingBtActive.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
src/assets/images/TopList/kingFrame.png
Normal file
|
After Width: | Height: | Size: 268 KiB |
BIN
src/assets/images/TopList/listTitle.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
src/assets/images/TopList/queenBt.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
src/assets/images/TopList/queenBtActive.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
src/assets/images/TopList/queenFrame.png
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
src/assets/images/TopList/rewards.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
@ -1,206 +1,212 @@
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import { installRouteGuard } from "../utils/routeGuard.js";
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { installRouteGuard } from '../utils/routeGuard.js'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "root",
|
||||
component: () => import("../views/LoadingView.vue"),
|
||||
path: '/',
|
||||
name: 'root',
|
||||
component: () => import('../views/LoadingView.vue'),
|
||||
meta: { requiresAuth: false, isPublic: true },
|
||||
},
|
||||
{
|
||||
path: "/loading",
|
||||
name: "loading",
|
||||
component: () => import("../views/LoadingView.vue"),
|
||||
path: '/loading',
|
||||
name: 'loading',
|
||||
component: () => import('../views/LoadingView.vue'),
|
||||
meta: { requiresAuth: false, isPublic: true },
|
||||
},
|
||||
{
|
||||
path: "/host-center",
|
||||
name: "host-center",
|
||||
component: () => import("../views/HostCenterView.vue"),
|
||||
path: '/host-center',
|
||||
name: 'host-center',
|
||||
component: () => import('../views/HostCenterView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/agency-center",
|
||||
name: "agency-center",
|
||||
component: () => import("../views/AgencyCenterView.vue"),
|
||||
path: '/agency-center',
|
||||
name: 'agency-center',
|
||||
component: () => import('../views/AgencyCenterView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/bd-center",
|
||||
name: "bd-center",
|
||||
component: () => import("../views/BDCenterView.vue"),
|
||||
path: '/bd-center',
|
||||
name: 'bd-center',
|
||||
component: () => import('../views/BDCenterView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/coin-seller",
|
||||
name: "coin-seller",
|
||||
component: () => import("../views/CoinSellerView.vue"),
|
||||
path: '/coin-seller',
|
||||
name: 'coin-seller',
|
||||
component: () => import('../views/CoinSellerView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/apply",
|
||||
name: "apply",
|
||||
component: () => import("../views/ApplyView.vue"),
|
||||
path: '/apply',
|
||||
name: 'apply',
|
||||
component: () => import('../views/ApplyView.vue'),
|
||||
meta: { requiresAuth: false },
|
||||
},
|
||||
{
|
||||
path: "/message",
|
||||
name: "message",
|
||||
component: () => import("../views/MessageView.vue"),
|
||||
path: '/message',
|
||||
name: 'message',
|
||||
component: () => import('../views/MessageView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/transfer",
|
||||
name: "transfer",
|
||||
component: () => import("../views/TransferView.vue"),
|
||||
path: '/transfer',
|
||||
name: 'transfer',
|
||||
component: () => import('../views/TransferView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/exchange-gold-coins",
|
||||
name: "exchange-gold-coins",
|
||||
component: () => import("../views/ExchangeGoldCoinsView.vue"),
|
||||
path: '/exchange-gold-coins',
|
||||
name: 'exchange-gold-coins',
|
||||
component: () => import('../views/ExchangeGoldCoinsView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
// 辅助页面
|
||||
{
|
||||
path: "/about",
|
||||
name: "about",
|
||||
component: () => import("../views/AboutView.vue"),
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
component: () => import('../views/AboutView.vue'),
|
||||
},
|
||||
{
|
||||
path: "/host-setting",
|
||||
name: "host-setting",
|
||||
component: () => import("../views/HostSettingView.vue"),
|
||||
path: '/host-setting',
|
||||
name: 'host-setting',
|
||||
component: () => import('../views/HostSettingView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/search-payee",
|
||||
name: "search-payee",
|
||||
component: () => import("../views/SearchPayeeView.vue"),
|
||||
path: '/search-payee',
|
||||
name: 'search-payee',
|
||||
component: () => import('../views/SearchPayeeView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/information-details",
|
||||
name: "information-details",
|
||||
component: () => import("../views/InformationDetailsView.vue"),
|
||||
path: '/information-details',
|
||||
name: 'information-details',
|
||||
component: () => import('../views/InformationDetailsView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/invite-members",
|
||||
name: "invite-members",
|
||||
component: () => import("../views/InviteMembersView.vue"),
|
||||
path: '/invite-members',
|
||||
name: 'invite-members',
|
||||
component: () => import('../views/InviteMembersView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/team-bill",
|
||||
name: "team-bill",
|
||||
component: () => import("../views/TeamBillView.vue"),
|
||||
path: '/team-bill',
|
||||
name: 'team-bill',
|
||||
component: () => import('../views/TeamBillView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/team-member",
|
||||
name: "team-member",
|
||||
component: () => import("../views/TeamMemberView.vue"),
|
||||
path: '/team-member',
|
||||
name: 'team-member',
|
||||
component: () => import('../views/TeamMemberView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/platform-policy",
|
||||
name: "platform-policy",
|
||||
component: () => import("../views/PlatformPolicyView.vue"),
|
||||
path: '/platform-policy',
|
||||
name: 'platform-policy',
|
||||
component: () => import('../views/PlatformPolicyView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/coin-seller-search",
|
||||
name: "coin-seller-search",
|
||||
component: () => import("../views/CoinSellerSearchView.vue"),
|
||||
path: '/coin-seller-search',
|
||||
name: 'coin-seller-search',
|
||||
component: () => import('../views/CoinSellerSearchView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/seller-records",
|
||||
name: "seller-records",
|
||||
component: () => import("../views/SellerRecordsView.vue"),
|
||||
path: '/seller-records',
|
||||
name: 'seller-records',
|
||||
component: () => import('../views/SellerRecordsView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/bd-setting",
|
||||
name: "bd-setting",
|
||||
component: () => import("../views/BDSettingView.vue"),
|
||||
path: '/bd-setting',
|
||||
name: 'bd-setting',
|
||||
component: () => import('../views/BDSettingView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
// 公共页面
|
||||
{
|
||||
path: "/not_app",
|
||||
name: "not-app",
|
||||
component: () => import("../views/NotAppView.vue"),
|
||||
path: '/not_app',
|
||||
name: 'not-app',
|
||||
component: () => import('../views/NotAppView.vue'),
|
||||
meta: { requiresAuth: false },
|
||||
},
|
||||
// 404 页面
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
name: "not-found",
|
||||
redirect: "/apply",
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'not-found',
|
||||
redirect: '/apply',
|
||||
},
|
||||
{
|
||||
path: "/history-salary",
|
||||
name: "history-salary",
|
||||
component: () => import("../views/HistorySalaryView.vue"),
|
||||
path: '/history-salary',
|
||||
name: 'history-salary',
|
||||
component: () => import('../views/HistorySalaryView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/platform-salary",
|
||||
name: "platform-salary",
|
||||
component: () => import("../views/PlatformPolicyView.vue"),
|
||||
path: '/platform-salary',
|
||||
name: 'platform-salary',
|
||||
component: () => import('../views/PlatformPolicyView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/admin-center",
|
||||
name: "admin-center",
|
||||
component: () => import("../views/AdminCenter.vue"),
|
||||
path: '/admin-center',
|
||||
name: 'admin-center',
|
||||
component: () => import('../views/AdminCenter.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/item-distribution",
|
||||
name: "item-distribution",
|
||||
component: () => import("../views/ItemDistribution.vue"),
|
||||
path: '/item-distribution',
|
||||
name: 'item-distribution',
|
||||
component: () => import('../views/ItemDistribution.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/recharge",
|
||||
name: "recharge",
|
||||
component: () => import("../views/Recharge.vue"),
|
||||
path: '/recharge',
|
||||
name: 'recharge',
|
||||
component: () => import('../views/Recharge.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/recharge-standard",
|
||||
name: "recharge-standard",
|
||||
component: () => import("../views/RechargeStandard.vue"),
|
||||
path: '/recharge-standard',
|
||||
name: 'recharge-standard',
|
||||
component: () => import('../views/RechargeStandard.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/recharge-freight-agent",
|
||||
name: "recharge-freight-agent",
|
||||
component: () => import("../views/RechargeFreightAgent.vue"),
|
||||
path: '/recharge-freight-agent',
|
||||
name: 'recharge-freight-agent',
|
||||
component: () => import('../views/RechargeFreightAgent.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/map",
|
||||
name: "map",
|
||||
component: () => import("../views/map.vue"),
|
||||
path: '/map',
|
||||
name: 'map',
|
||||
component: () => import('../views/map.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/pay-result",
|
||||
name: "pay-result",
|
||||
component: () => import("../views/payResult.vue"),
|
||||
path: '/pay-result',
|
||||
name: 'pay-result',
|
||||
component: () => import('../views/payResult.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/top-list',
|
||||
name: 'top-list',
|
||||
component: () => import('../views/TopList/TopList.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
// 安装路由守卫
|
||||
installRouteGuard(router);
|
||||
installRouteGuard(router)
|
||||
|
||||
export default router;
|
||||
export default router
|
||||
|
||||
@ -5,33 +5,33 @@
|
||||
|
||||
// 用户身份类型
|
||||
export const USER_ROLES = {
|
||||
FREIGHT_AGENT: "freightAgent",
|
||||
AGENT: "agent",
|
||||
BD: "bd",
|
||||
ANCHOR: "anchor",
|
||||
GUEST: "guest",
|
||||
ADMIN: "admin",
|
||||
};
|
||||
FREIGHT_AGENT: 'freightAgent',
|
||||
AGENT: 'agent',
|
||||
BD: 'bd',
|
||||
ANCHOR: 'anchor',
|
||||
GUEST: 'guest',
|
||||
ADMIN: 'admin',
|
||||
}
|
||||
|
||||
// 页面路径常量
|
||||
export const PAGES = {
|
||||
COIN_SELLER: "/coin-seller",
|
||||
AGENCY_CENTER: "/agency-center",
|
||||
BD_CENTER: "/bd-center",
|
||||
HOST_CENTER: "/host-center",
|
||||
APPLY: "/apply",
|
||||
MESSAGE: "/message",
|
||||
TRANSFER: "/transfer",
|
||||
EXCHANGE: "/exchange-gold-coins",
|
||||
PERSONAL_SALARY: "/platform-policy",
|
||||
HOST_SETTING: "/host-setting",
|
||||
NOT_APP: "/not_app",
|
||||
LOADING: "/loading",
|
||||
ADMIN_CENTER: "/admin-center",
|
||||
RECHARGE_STANDARD: "/recharge-standard",
|
||||
PAY_RESULT: "/pay-result",
|
||||
MAP: "/map",
|
||||
};
|
||||
COIN_SELLER: '/coin-seller',
|
||||
AGENCY_CENTER: '/agency-center',
|
||||
BD_CENTER: '/bd-center',
|
||||
HOST_CENTER: '/host-center',
|
||||
APPLY: '/apply',
|
||||
MESSAGE: '/message',
|
||||
TRANSFER: '/transfer',
|
||||
EXCHANGE: '/exchange-gold-coins',
|
||||
PERSONAL_SALARY: '/platform-policy',
|
||||
HOST_SETTING: '/host-setting',
|
||||
NOT_APP: '/not_app',
|
||||
LOADING: '/loading',
|
||||
ADMIN_CENTER: '/admin-center',
|
||||
RECHARGE_STANDARD: '/recharge-standard',
|
||||
PAY_RESULT: '/pay-result',
|
||||
MAP: '/map',
|
||||
}
|
||||
|
||||
// 🎯 核心改变:基于身份的权限配置
|
||||
// 每个身份拥有的页面权限列表
|
||||
@ -39,9 +39,9 @@ export const ROLE_PERMISSIONS = {
|
||||
// Freight Agent (货运代理/金币销售员)
|
||||
[USER_ROLES.FREIGHT_AGENT]: [
|
||||
PAGES.COIN_SELLER, // 主页面
|
||||
"/seller-records",
|
||||
"/coin-seller-search",
|
||||
"/recharge-freight-agent",
|
||||
'/seller-records',
|
||||
'/coin-seller-search',
|
||||
'/recharge-freight-agent',
|
||||
],
|
||||
|
||||
// Agent (代理商)
|
||||
@ -52,13 +52,13 @@ export const ROLE_PERMISSIONS = {
|
||||
PAGES.MESSAGE, // 消息功能
|
||||
PAGES.PERSONAL_SALARY, // 个人薪资查看
|
||||
PAGES.RECHARGE_STANDARD, //普通转账功能
|
||||
"/team-member",
|
||||
"/information-details",
|
||||
"/search-payee",
|
||||
"/history-salary",
|
||||
"/platform-salary",
|
||||
"/invite-members",
|
||||
"/team-bill",
|
||||
'/team-member',
|
||||
'/information-details',
|
||||
'/search-payee',
|
||||
'/history-salary',
|
||||
'/platform-salary',
|
||||
'/invite-members',
|
||||
'/team-bill',
|
||||
],
|
||||
|
||||
// BD (商务拓展)
|
||||
@ -68,9 +68,9 @@ export const ROLE_PERMISSIONS = {
|
||||
PAGES.EXCHANGE, // 兑换功能
|
||||
PAGES.MESSAGE, // 消息功能
|
||||
PAGES.RECHARGE_STANDARD, //普通转账功能
|
||||
"/team-member",
|
||||
"/history-salary",
|
||||
"/platform-salary",
|
||||
'/team-member',
|
||||
'/history-salary',
|
||||
'/platform-salary',
|
||||
],
|
||||
|
||||
// Anchor (主播)
|
||||
@ -82,19 +82,19 @@ export const ROLE_PERMISSIONS = {
|
||||
PAGES.PERSONAL_SALARY, // 个人薪资查看
|
||||
PAGES.HOST_SETTING, // 主播设置
|
||||
PAGES.RECHARGE_STANDARD, //普通转账功能
|
||||
"/information-details",
|
||||
"/search-payee",
|
||||
"/apply",
|
||||
"/history-salary",
|
||||
"/platform-salary",
|
||||
"/invite-members",
|
||||
"/team-bill",
|
||||
'/information-details',
|
||||
'/search-payee',
|
||||
'/apply',
|
||||
'/history-salary',
|
||||
'/platform-salary',
|
||||
'/invite-members',
|
||||
'/team-bill',
|
||||
],
|
||||
|
||||
// Admin(管理员)
|
||||
[USER_ROLES.ADMIN]: [
|
||||
PAGES.ADMIN_CENTER, //管理员中心
|
||||
"/item-distribution", //赠送商品页面
|
||||
'/item-distribution', //赠送商品页面
|
||||
PAGES.RECHARGE_STANDARD, //普通转账功能
|
||||
],
|
||||
|
||||
@ -108,13 +108,14 @@ export const ROLE_PERMISSIONS = {
|
||||
PAGES.NOT_APP, // 错误页面
|
||||
PAGES.PAY_RESULT, // 支付成功页面
|
||||
PAGES.MAP, // 地图选择页面
|
||||
"/recharge",
|
||||
"/recharge-freight-agent",
|
||||
'/recharge',
|
||||
'/recharge-freight-agent',
|
||||
'/top-list',
|
||||
],
|
||||
|
||||
// 加载页面
|
||||
LOADING_PAGE: [PAGES.LOADING],
|
||||
};
|
||||
}
|
||||
|
||||
// 身份优先级(数字越大优先级越高)
|
||||
export const ROLE_PRIORITY = {
|
||||
@ -124,7 +125,7 @@ export const ROLE_PRIORITY = {
|
||||
[USER_ROLES.AGENT]: 2,
|
||||
[USER_ROLES.ANCHOR]: 1,
|
||||
[USER_ROLES.GUEST]: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// 身份对应的默认首页
|
||||
export const ROLE_DEFAULT_PAGES = {
|
||||
@ -134,52 +135,52 @@ export const ROLE_DEFAULT_PAGES = {
|
||||
[USER_ROLES.BD]: PAGES.BD_CENTER,
|
||||
[USER_ROLES.ANCHOR]: PAGES.HOST_CENTER,
|
||||
[USER_ROLES.GUEST]: PAGES.APPLY,
|
||||
};
|
||||
}
|
||||
|
||||
// 身份显示配置
|
||||
export const ROLE_DISPLAY_CONFIG = {
|
||||
[USER_ROLES.FREIGHT_AGENT]: {
|
||||
name: "Freight Agent",
|
||||
tag: "💰 Coin Seller",
|
||||
color: "#FCD34D",
|
||||
bgColor: "#FEF3C7",
|
||||
name: 'Freight Agent',
|
||||
tag: '💰 Coin Seller',
|
||||
color: '#FCD34D',
|
||||
bgColor: '#FEF3C7',
|
||||
},
|
||||
[USER_ROLES.AGENT]: {
|
||||
name: "Agent",
|
||||
tag: "🏢 Agency",
|
||||
color: "#1E40AF",
|
||||
bgColor: "#DBEAFE",
|
||||
name: 'Agent',
|
||||
tag: '🏢 Agency',
|
||||
color: '#1E40AF',
|
||||
bgColor: '#DBEAFE',
|
||||
},
|
||||
[USER_ROLES.BD]: {
|
||||
name: "BD",
|
||||
tag: "📈 BD",
|
||||
color: "#059669",
|
||||
bgColor: "#D1FAE5",
|
||||
name: 'BD',
|
||||
tag: '📈 BD',
|
||||
color: '#059669',
|
||||
bgColor: '#D1FAE5',
|
||||
},
|
||||
[USER_ROLES.ANCHOR]: {
|
||||
name: "Anchor",
|
||||
tag: "👑 Host",
|
||||
color: "#5B21B6",
|
||||
bgColor: "#E0E7FF",
|
||||
name: 'Anchor',
|
||||
tag: '👑 Host',
|
||||
color: '#5B21B6',
|
||||
bgColor: '#E0E7FF',
|
||||
},
|
||||
[USER_ROLES.GUEST]: {
|
||||
name: "Guest",
|
||||
tag: "👤 Guest",
|
||||
color: "#6B7280",
|
||||
bgColor: "#F3F4F6",
|
||||
name: 'Guest',
|
||||
tag: '👤 Guest',
|
||||
color: '#6B7280',
|
||||
bgColor: '#F3F4F6',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于身份的权限管理类
|
||||
*/
|
||||
class RoleBasedPermissionManager {
|
||||
constructor() {
|
||||
this.userIdentity = null;
|
||||
this.primaryRole = null;
|
||||
this.allRoles = [];
|
||||
this.isIdentityLoaded = false;
|
||||
this.allowedPages = [];
|
||||
this.userIdentity = null
|
||||
this.primaryRole = null
|
||||
this.allRoles = []
|
||||
this.isIdentityLoaded = false
|
||||
this.allowedPages = []
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,18 +188,18 @@ class RoleBasedPermissionManager {
|
||||
* @param {Object} identity - 身份信息对象
|
||||
*/
|
||||
setUserIdentity(identity) {
|
||||
this.userIdentity = identity;
|
||||
this.allRoles = this.calculateAllRoles(identity);
|
||||
this.primaryRole = this.calculatePrimaryRole(this.allRoles);
|
||||
this.allowedPages = this.calculateAllowedPages(this.allRoles);
|
||||
this.isIdentityLoaded = true;
|
||||
this.userIdentity = identity
|
||||
this.allRoles = this.calculateAllRoles(identity)
|
||||
this.primaryRole = this.calculatePrimaryRole(this.allRoles)
|
||||
this.allowedPages = this.calculateAllowedPages(this.allRoles)
|
||||
this.isIdentityLoaded = true
|
||||
|
||||
console.debug("🔐 User identity updated:", {
|
||||
console.debug('🔐 User identity updated:', {
|
||||
identity: this.userIdentity,
|
||||
allRoles: this.allRoles,
|
||||
primaryRole: this.primaryRole,
|
||||
allowedPages: this.allowedPages,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,16 +208,16 @@ class RoleBasedPermissionManager {
|
||||
* @returns {Array} 所有身份列表
|
||||
*/
|
||||
calculateAllRoles(identity) {
|
||||
if (!identity) return [USER_ROLES.GUEST];
|
||||
if (!identity) return [USER_ROLES.GUEST]
|
||||
|
||||
const roles = [];
|
||||
if (identity.freightAgent) roles.push(USER_ROLES.FREIGHT_AGENT);
|
||||
if (identity.agent) roles.push(USER_ROLES.AGENT);
|
||||
if (identity.bd) roles.push(USER_ROLES.BD);
|
||||
if (identity.anchor) roles.push(USER_ROLES.ANCHOR);
|
||||
if (identity.admin) roles.push(USER_ROLES.ADMIN);
|
||||
const roles = []
|
||||
if (identity.freightAgent) roles.push(USER_ROLES.FREIGHT_AGENT)
|
||||
if (identity.agent) roles.push(USER_ROLES.AGENT)
|
||||
if (identity.bd) roles.push(USER_ROLES.BD)
|
||||
if (identity.anchor) roles.push(USER_ROLES.ANCHOR)
|
||||
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} 主要身份
|
||||
*/
|
||||
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 ROLE_PRIORITY[current] > ROLE_PRIORITY[highest] ? current : highest;
|
||||
}, USER_ROLES.GUEST);
|
||||
return ROLE_PRIORITY[current] > ROLE_PRIORITY[highest] ? current : highest
|
||||
}, USER_ROLES.GUEST)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,27 +240,27 @@ class RoleBasedPermissionManager {
|
||||
* @returns {Array} 允许访问的页面列表
|
||||
*/
|
||||
calculateAllowedPages(roles) {
|
||||
const allowedPages = new Set();
|
||||
const allowedPages = new Set()
|
||||
|
||||
// 添加公共页面
|
||||
ROLE_PERMISSIONS.PUBLIC_PAGES.forEach((page) => {
|
||||
allowedPages.add(page);
|
||||
});
|
||||
allowedPages.add(page)
|
||||
})
|
||||
|
||||
// 添加loading页面
|
||||
ROLE_PERMISSIONS.LOADING_PAGE.forEach((page) => {
|
||||
allowedPages.add(page);
|
||||
});
|
||||
allowedPages.add(page)
|
||||
})
|
||||
|
||||
// 根据用户拥有的每个身份,添加对应的页面权限
|
||||
roles.forEach((role) => {
|
||||
const rolePages = ROLE_PERMISSIONS[role] || [];
|
||||
const rolePages = ROLE_PERMISSIONS[role] || []
|
||||
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) {
|
||||
if (!this.isIdentityLoaded) {
|
||||
console.warn("⚠️ Identity not loaded yet");
|
||||
return false;
|
||||
console.warn('⚠️ Identity not loaded yet')
|
||||
return false
|
||||
}
|
||||
|
||||
const hasPermission = this.allowedPages.includes(page);
|
||||
const hasPermission = this.allowedPages.includes(page)
|
||||
|
||||
console.debug("🔍 Permission check:", {
|
||||
console.debug('🔍 Permission check:', {
|
||||
page,
|
||||
allRoles: this.allRoles,
|
||||
primaryRole: this.primaryRole,
|
||||
hasPermission,
|
||||
});
|
||||
})
|
||||
|
||||
return hasPermission;
|
||||
return hasPermission
|
||||
}
|
||||
|
||||
/**
|
||||
@ -292,10 +293,10 @@ class RoleBasedPermissionManager {
|
||||
* @returns {boolean} 是否有权限
|
||||
*/
|
||||
hasRolePagePermission(role, page) {
|
||||
const rolePages = ROLE_PERMISSIONS[role] || [];
|
||||
const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || [];
|
||||
const rolePages = ROLE_PERMISSIONS[role] || []
|
||||
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} 页面权限列表
|
||||
*/
|
||||
getRolePermissions(role) {
|
||||
const rolePages = ROLE_PERMISSIONS[role] || [];
|
||||
const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || [];
|
||||
const rolePages = ROLE_PERMISSIONS[role] || []
|
||||
const publicPages = ROLE_PERMISSIONS.PUBLIC_PAGES || []
|
||||
|
||||
return [...new Set([...rolePages, ...publicPages])];
|
||||
return [...new Set([...rolePages, ...publicPages])]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +316,7 @@ class RoleBasedPermissionManager {
|
||||
* @returns {string} 默认页面路径
|
||||
*/
|
||||
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} 当前主要身份
|
||||
*/
|
||||
getPrimaryRole() {
|
||||
return this.primaryRole;
|
||||
return this.primaryRole
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,7 +332,7 @@ class RoleBasedPermissionManager {
|
||||
* @returns {Array} 所有身份
|
||||
*/
|
||||
getAllRoles() {
|
||||
return this.allRoles;
|
||||
return this.allRoles
|
||||
}
|
||||
|
||||
/**
|
||||
@ -340,7 +341,7 @@ class RoleBasedPermissionManager {
|
||||
* @returns {Object} 显示信息
|
||||
*/
|
||||
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} 是否有多重身份
|
||||
*/
|
||||
hasMultipleRoles() {
|
||||
return this.allRoles.length > 1;
|
||||
return this.allRoles.length > 1
|
||||
}
|
||||
|
||||
/**
|
||||
@ -358,11 +359,11 @@ class RoleBasedPermissionManager {
|
||||
*/
|
||||
switchToRole(role) {
|
||||
if (!this.allRoles.includes(role)) {
|
||||
console.warn(`⚠️ User does not have role: ${role}`);
|
||||
return this.getDefaultPage();
|
||||
console.warn(`⚠️ User does not have role: ${role}`)
|
||||
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} 可访问的页面菜单
|
||||
*/
|
||||
getNavigationMenu() {
|
||||
const menu = [];
|
||||
const menu = []
|
||||
|
||||
this.allowedPages.forEach((page) => {
|
||||
const menuItem = this.getPageMenuInfo(page);
|
||||
const menuItem = this.getPageMenuInfo(page)
|
||||
if (menuItem) {
|
||||
menu.push(menuItem);
|
||||
menu.push(menuItem)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
return menu;
|
||||
return menu
|
||||
}
|
||||
|
||||
/**
|
||||
@ -389,31 +390,31 @@ class RoleBasedPermissionManager {
|
||||
*/
|
||||
getPageMenuInfo(page) {
|
||||
const pageMenuConfig = {
|
||||
[PAGES.COIN_SELLER]: { title: "Coin Seller", icon: "💰", order: 1 },
|
||||
[PAGES.AGENCY_CENTER]: { title: "Agency Center", icon: "🏢", order: 2 },
|
||||
[PAGES.BD_CENTER]: { title: "BD Center", icon: "📈", order: 3 },
|
||||
[PAGES.HOST_CENTER]: { title: "Host Center", icon: "👑", order: 4 },
|
||||
[PAGES.TRANSFER]: { title: "Transfer", icon: "💸", order: 5 },
|
||||
[PAGES.EXCHANGE]: { title: "Exchange", icon: "🔄", order: 6 },
|
||||
[PAGES.MESSAGE]: { title: "Messages", icon: "💬", order: 7 },
|
||||
[PAGES.PERSONAL_SALARY]: { title: "Salary", icon: "💵", order: 8 },
|
||||
[PAGES.HOST_SETTING]: { title: "Settings", icon: "⚙️", order: 9 },
|
||||
};
|
||||
[PAGES.COIN_SELLER]: { title: 'Coin Seller', icon: '💰', order: 1 },
|
||||
[PAGES.AGENCY_CENTER]: { title: 'Agency Center', icon: '🏢', order: 2 },
|
||||
[PAGES.BD_CENTER]: { title: 'BD Center', icon: '📈', order: 3 },
|
||||
[PAGES.HOST_CENTER]: { title: 'Host Center', icon: '👑', order: 4 },
|
||||
[PAGES.TRANSFER]: { title: 'Transfer', icon: '💸', order: 5 },
|
||||
[PAGES.EXCHANGE]: { title: 'Exchange', icon: '🔄', order: 6 },
|
||||
[PAGES.MESSAGE]: { title: 'Messages', icon: '💬', order: 7 },
|
||||
[PAGES.PERSONAL_SALARY]: { title: 'Salary', icon: '💵', order: 8 },
|
||||
[PAGES.HOST_SETTING]: { title: 'Settings', icon: '⚙️', order: 9 },
|
||||
}
|
||||
|
||||
const config = pageMenuConfig[page];
|
||||
return config ? { ...config, path: page } : null;
|
||||
const config = pageMenuConfig[page]
|
||||
return config ? { ...config, path: page } : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置权限信息
|
||||
*/
|
||||
reset() {
|
||||
this.userIdentity = null;
|
||||
this.primaryRole = null;
|
||||
this.allRoles = [];
|
||||
this.isIdentityLoaded = false;
|
||||
this.allowedPages = [];
|
||||
console.debug("🔄 Permission manager reset");
|
||||
this.userIdentity = null
|
||||
this.primaryRole = null
|
||||
this.allRoles = []
|
||||
this.isIdentityLoaded = false
|
||||
this.allowedPages = []
|
||||
console.debug('🔄 Permission manager reset')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -421,41 +422,40 @@ class RoleBasedPermissionManager {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLoaded() {
|
||||
return this.isIdentityLoaded;
|
||||
return this.isIdentityLoaded
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试方法:打印当前权限状态
|
||||
*/
|
||||
debugPermissions() {
|
||||
console.group("🔐 Current Permission State");
|
||||
console.log("User Identity:", this.userIdentity);
|
||||
console.log("All Roles:", this.allRoles);
|
||||
console.log("Primary Role:", this.primaryRole);
|
||||
console.log("Allowed Pages:", this.allowedPages);
|
||||
console.log("Default Page:", this.getDefaultPage());
|
||||
console.log("Navigation Menu:", this.getNavigationMenu());
|
||||
console.groupEnd();
|
||||
console.group('🔐 Current Permission State')
|
||||
console.log('User Identity:', this.userIdentity)
|
||||
console.log('All Roles:', this.allRoles)
|
||||
console.log('Primary Role:', this.primaryRole)
|
||||
console.log('Allowed Pages:', this.allowedPages)
|
||||
console.log('Default Page:', this.getDefaultPage())
|
||||
console.log('Navigation Menu:', this.getNavigationMenu())
|
||||
console.groupEnd()
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局权限管理实例
|
||||
export const permissionManager = new RoleBasedPermissionManager();
|
||||
export const permissionManager = new RoleBasedPermissionManager()
|
||||
|
||||
// 便捷方法
|
||||
export const hasPermission = (page) => permissionManager.hasPagePermission(page);
|
||||
export const hasRolePermission = (role, page) =>
|
||||
permissionManager.hasRolePagePermission(role, page);
|
||||
export const getRolePermissions = (role) => permissionManager.getRolePermissions(role);
|
||||
export const getDefaultPage = () => permissionManager.getDefaultPage();
|
||||
export const getPrimaryRole = () => permissionManager.getPrimaryRole();
|
||||
export const getAllRoles = () => permissionManager.getAllRoles();
|
||||
export const getRoleDisplayInfo = (role) => permissionManager.getRoleDisplayInfo(role);
|
||||
export const setUserIdentity = (identity) => permissionManager.setUserIdentity(identity);
|
||||
export const resetPermissions = () => permissionManager.reset();
|
||||
export const switchToRole = (role) => permissionManager.switchToRole(role);
|
||||
export const getNavigationMenu = () => permissionManager.getNavigationMenu();
|
||||
export const debugPermissions = () => permissionManager.debugPermissions();
|
||||
export const hasPermission = (page) => permissionManager.hasPagePermission(page)
|
||||
export const hasRolePermission = (role, page) => permissionManager.hasRolePagePermission(role, page)
|
||||
export const getRolePermissions = (role) => permissionManager.getRolePermissions(role)
|
||||
export const getDefaultPage = () => permissionManager.getDefaultPage()
|
||||
export const getPrimaryRole = () => permissionManager.getPrimaryRole()
|
||||
export const getAllRoles = () => permissionManager.getAllRoles()
|
||||
export const getRoleDisplayInfo = (role) => permissionManager.getRoleDisplayInfo(role)
|
||||
export const setUserIdentity = (identity) => permissionManager.setUserIdentity(identity)
|
||||
export const resetPermissions = () => permissionManager.reset()
|
||||
export const switchToRole = (role) => permissionManager.switchToRole(role)
|
||||
export const getNavigationMenu = () => permissionManager.getNavigationMenu()
|
||||
export const debugPermissions = () => permissionManager.debugPermissions()
|
||||
|
||||
// 🎯 新增:快速查询某个身份的权限
|
||||
export const QUICK_ROLE_CHECK = {
|
||||
@ -482,4 +482,4 @@ export const QUICK_ROLE_CHECK = {
|
||||
pages: getRolePermissions(USER_ROLES.ANCHOR),
|
||||
canAccess: (page) => hasRolePermission(USER_ROLES.ANCHOR, page),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,24 +3,24 @@
|
||||
* 提供APP连接检查、页面访问权限控制和自动重定向功能
|
||||
*/
|
||||
|
||||
import { permissionManager, PAGES, USER_ROLES } from "./permissionManager.js";
|
||||
import { getUserIdentity, getMemberProfile } from "../api/wallet.js";
|
||||
import { getUserId, setUserInfo } from "./userStore.js";
|
||||
import { permissionManager, PAGES, USER_ROLES } from './permissionManager.js'
|
||||
import { getUserIdentity, getMemberProfile } from '../api/wallet.js'
|
||||
import { getUserId, setUserInfo } from './userStore.js'
|
||||
import {
|
||||
connectApplication,
|
||||
parseAccessOrigin,
|
||||
parseHeader,
|
||||
setHttpHeaders,
|
||||
isInApp,
|
||||
} from "./appBridge.js";
|
||||
import { appConnectionManager, isAppConnected } from "./appConnectionManager.js";
|
||||
} from './appBridge.js'
|
||||
import { appConnectionManager, isAppConnected } from './appConnectionManager.js'
|
||||
|
||||
/**
|
||||
* APP连接检查器
|
||||
*/
|
||||
class AppConnectionChecker {
|
||||
constructor() {
|
||||
this.isConnecting = false;
|
||||
this.isConnecting = false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,38 +28,38 @@ class AppConnectionChecker {
|
||||
* @returns {Promise<boolean>} 连接是否成功
|
||||
*/
|
||||
async ensureConnection() {
|
||||
console.debug("🔗 Checking APP connection...");
|
||||
console.debug('🔗 Checking APP connection...')
|
||||
|
||||
// 如果不在APP环境中,直接返回成功
|
||||
|
||||
console.log("我是app环境", window.app);
|
||||
console.log("我是app环境", window.webkit);
|
||||
console.log("我是app环境", window.FlutterPageControl);
|
||||
console.log('我是app环境', window.app)
|
||||
console.log('我是app环境', window.webkit)
|
||||
console.log('我是app环境', window.FlutterPageControl)
|
||||
if (!isInApp()) {
|
||||
console.debug("🌐 Browser environment, no connection needed");
|
||||
return true;
|
||||
console.debug('🌐 Browser environment, no connection needed')
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果已经连接且未超时,直接返回成功
|
||||
if (isAppConnected()) {
|
||||
console.debug("✅ APP already connected");
|
||||
return true;
|
||||
console.debug('✅ APP already connected')
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果正在连接中,等待现有连接
|
||||
if (appConnectionManager.isConnecting()) {
|
||||
console.debug("⏳ Connection already in progress, waiting...");
|
||||
console.debug('⏳ Connection already in progress, waiting...')
|
||||
try {
|
||||
await appConnectionManager.getConnectionPromise();
|
||||
return isAppConnected();
|
||||
await appConnectionManager.getConnectionPromise()
|
||||
return isAppConnected()
|
||||
} catch (error) {
|
||||
console.error("❌ Connection wait failed:", error);
|
||||
return false;
|
||||
console.error('❌ Connection wait failed:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 开始新的连接
|
||||
return await this._performConnection();
|
||||
return await this._performConnection()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,51 +68,51 @@ class AppConnectionChecker {
|
||||
*/
|
||||
async _performConnection() {
|
||||
try {
|
||||
console.debug("📱 Starting new APP connection...");
|
||||
console.debug('📱 Starting new APP connection...')
|
||||
|
||||
const connectionPromise = new Promise((resolve, reject) => {
|
||||
connectApplication(async (access) => {
|
||||
try {
|
||||
const result = parseAccessOrigin(access);
|
||||
const result = parseAccessOrigin(access)
|
||||
|
||||
if (result.success) {
|
||||
// 解析头部信息
|
||||
const headerInfo = parseHeader(result.data);
|
||||
const headerInfo = parseHeader(result.data)
|
||||
|
||||
// 设置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连接成功后立即获取用户信息
|
||||
await this._fetchUserInfoAfterConnection();
|
||||
await this._fetchUserInfoAfterConnection()
|
||||
|
||||
resolve(true);
|
||||
resolve(true)
|
||||
} else {
|
||||
console.error("❌ Failed to parse access origin:", result.error);
|
||||
appConnectionManager.reset();
|
||||
reject(new Error(result.error));
|
||||
console.error('❌ Failed to parse access origin:', result.error)
|
||||
appConnectionManager.reset()
|
||||
reject(new Error(result.error))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Connection process failed:", error);
|
||||
appConnectionManager.reset();
|
||||
reject(error);
|
||||
console.error('❌ Connection process failed:', error)
|
||||
appConnectionManager.reset()
|
||||
reject(error)
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
// 设置连接状态
|
||||
appConnectionManager.setConnecting(connectionPromise);
|
||||
appConnectionManager.setConnecting(connectionPromise)
|
||||
|
||||
// 等待连接完成
|
||||
return await connectionPromise;
|
||||
return await connectionPromise
|
||||
} catch (error) {
|
||||
console.error("❌ APP connection failed:", error);
|
||||
appConnectionManager.reset();
|
||||
return false;
|
||||
console.error('❌ APP connection failed:', error)
|
||||
appConnectionManager.reset()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,21 +122,21 @@ class AppConnectionChecker {
|
||||
*/
|
||||
async _fetchUserInfoAfterConnection() {
|
||||
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) {
|
||||
// 缓存用户信息
|
||||
console.debug("✅ User info cached successfully");
|
||||
setUserInfo(response.body, null);
|
||||
console.debug('✅ User info cached successfully')
|
||||
setUserInfo(response.body, null)
|
||||
} else {
|
||||
console.warn("⚠️ Failed to get team entry or invalid response");
|
||||
console.warn('⚠️ Failed to get team entry or invalid response')
|
||||
}
|
||||
} catch (error) {
|
||||
// 这里不抛出错误,因为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() {
|
||||
appConnectionManager.reset();
|
||||
appConnectionManager.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,8 +153,8 @@ class AppConnectionChecker {
|
||||
*/
|
||||
class IdentityChecker {
|
||||
constructor() {
|
||||
this.isChecking = false;
|
||||
this.checkPromise = null;
|
||||
this.isChecking = false
|
||||
this.checkPromise = null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,26 +164,26 @@ class IdentityChecker {
|
||||
*/
|
||||
async checkUserIdentity(userId) {
|
||||
if (!userId) {
|
||||
console.warn("⚠️ No user ID provided");
|
||||
permissionManager.setUserIdentity(null);
|
||||
return USER_ROLES.GUEST;
|
||||
console.warn('⚠️ No user ID provided')
|
||||
permissionManager.setUserIdentity(null)
|
||||
return USER_ROLES.GUEST
|
||||
}
|
||||
|
||||
// 如果正在检查中,等待现有的检查完成
|
||||
if (this.isChecking && this.checkPromise) {
|
||||
console.debug("⏳ Identity check already in progress, waiting...");
|
||||
return await this.checkPromise;
|
||||
console.debug('⏳ Identity check already in progress, waiting...')
|
||||
return await this.checkPromise
|
||||
}
|
||||
|
||||
this.isChecking = true;
|
||||
this.checkPromise = this._performIdentityCheck(userId);
|
||||
this.isChecking = true
|
||||
this.checkPromise = this._performIdentityCheck(userId)
|
||||
|
||||
try {
|
||||
const primaryRole = await this.checkPromise;
|
||||
return primaryRole;
|
||||
const primaryRole = await this.checkPromise
|
||||
return primaryRole
|
||||
} finally {
|
||||
this.isChecking = false;
|
||||
this.checkPromise = null;
|
||||
this.isChecking = false
|
||||
this.checkPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,27 +194,27 @@ class IdentityChecker {
|
||||
*/
|
||||
async _performIdentityCheck(userId) {
|
||||
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) {
|
||||
const identity = response.body;
|
||||
console.debug("✅ Identity fetched:", identity);
|
||||
const identity = response.body
|
||||
console.debug('✅ Identity fetched:', identity)
|
||||
|
||||
// 设置权限信息
|
||||
permissionManager.setUserIdentity(identity);
|
||||
permissionManager.setUserIdentity(identity)
|
||||
|
||||
return permissionManager.getPrimaryRole();
|
||||
return permissionManager.getPrimaryRole()
|
||||
} else {
|
||||
console.warn("⚠️ Invalid identity response");
|
||||
permissionManager.setUserIdentity(null);
|
||||
return USER_ROLES.GUEST;
|
||||
console.warn('⚠️ Invalid identity response')
|
||||
permissionManager.setUserIdentity(null)
|
||||
return USER_ROLES.GUEST
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to fetch user identity:", error);
|
||||
permissionManager.setUserIdentity(null);
|
||||
return USER_ROLES.GUEST;
|
||||
console.error('❌ Failed to fetch user identity:', error)
|
||||
permissionManager.setUserIdentity(null)
|
||||
return USER_ROLES.GUEST
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,8 +222,8 @@ class IdentityChecker {
|
||||
* 重置检查状态
|
||||
*/
|
||||
reset() {
|
||||
this.isChecking = false;
|
||||
this.checkPromise = null;
|
||||
this.isChecking = false
|
||||
this.checkPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,36 +239,36 @@ class PageRedirector {
|
||||
* @returns {Promise<boolean>} 是否进行了重定向
|
||||
*/
|
||||
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,
|
||||
currentPath,
|
||||
primaryRole,
|
||||
hasPermission,
|
||||
});
|
||||
})
|
||||
|
||||
if (hasPermission) {
|
||||
// 有权限访问目标页面
|
||||
if (targetPath !== currentPath) {
|
||||
console.debug("✅ Access granted, navigating to:", targetPath);
|
||||
return false; // 不需要重定向,让路由正常进行
|
||||
console.debug('✅ Access granted, navigating to:', targetPath)
|
||||
return false // 不需要重定向,让路由正常进行
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
} 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) {
|
||||
await router.replace(defaultPage);
|
||||
return true; // 已进行重定向
|
||||
await router.replace(defaultPage)
|
||||
return true // 已进行重定向
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,17 +278,17 @@ class PageRedirector {
|
||||
* @param {string} attemptedPage - 尝试访问的页面
|
||||
*/
|
||||
async handleUnauthorizedAccess(router, attemptedPage) {
|
||||
const primaryRole = permissionManager.getPrimaryRole();
|
||||
const defaultPage = permissionManager.getDefaultPage();
|
||||
const primaryRole = permissionManager.getPrimaryRole()
|
||||
const defaultPage = permissionManager.getDefaultPage()
|
||||
|
||||
console.warn("🚫 Unauthorized access attempt:", {
|
||||
console.warn('🚫 Unauthorized access attempt:', {
|
||||
attemptedPage,
|
||||
primaryRole,
|
||||
redirectingTo: defaultPage,
|
||||
});
|
||||
})
|
||||
|
||||
// 重定向到用户有权限的默认页面
|
||||
await router.replace(defaultPage);
|
||||
await router.replace(defaultPage)
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,10 +297,10 @@ class PageRedirector {
|
||||
*/
|
||||
class RouteGuard {
|
||||
constructor() {
|
||||
this.appConnectionChecker = new AppConnectionChecker();
|
||||
this.identityChecker = new IdentityChecker();
|
||||
this.pageRedirector = new PageRedirector();
|
||||
this.isGuardActive = false;
|
||||
this.appConnectionChecker = new AppConnectionChecker()
|
||||
this.identityChecker = new IdentityChecker()
|
||||
this.pageRedirector = new PageRedirector()
|
||||
this.isGuardActive = false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,11 +309,11 @@ class RouteGuard {
|
||||
*/
|
||||
install(router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
await this.handleRouteChange(to, from, next);
|
||||
});
|
||||
await this.handleRouteChange(to, from, next)
|
||||
})
|
||||
|
||||
this.isGuardActive = true;
|
||||
console.debug("🛡️ Route guard installed");
|
||||
this.isGuardActive = true
|
||||
console.debug('🛡️ Route guard installed')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -323,68 +323,68 @@ class RouteGuard {
|
||||
* @param {Function} next - 路由继续函数
|
||||
*/
|
||||
async handleRouteChange(to, from, next) {
|
||||
console.group("🛡️ Route Guard Check");
|
||||
console.debug("📍 Route change:", from.path, "→", to.path);
|
||||
console.group('🛡️ Route Guard Check')
|
||||
console.debug('📍 Route change:', from.path, '→', to.path)
|
||||
|
||||
try {
|
||||
// 1. 检查是否为公共页面(无需任何验证)
|
||||
if (this.isPublicPage(to.path)) {
|
||||
console.debug("🌐 Public page, allowing access");
|
||||
console.groupEnd();
|
||||
next();
|
||||
return;
|
||||
console.debug('🌐 Public page, allowing access')
|
||||
console.groupEnd()
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 确保APP连接
|
||||
console.debug("🔗 Ensuring APP connection...");
|
||||
const isConnected = await this.appConnectionChecker.ensureConnection();
|
||||
console.debug('🔗 Ensuring APP connection...')
|
||||
const isConnected = await this.appConnectionChecker.ensureConnection()
|
||||
|
||||
if (!isConnected) {
|
||||
console.error("❌ APP connection failed, redirecting to error page");
|
||||
console.groupEnd();
|
||||
next(PAGES.NOT_APP);
|
||||
return;
|
||||
console.error('❌ APP connection failed, redirecting to error page')
|
||||
console.groupEnd()
|
||||
next(PAGES.NOT_APP)
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 检查用户身份是否已加载
|
||||
if (!permissionManager.isLoaded()) {
|
||||
console.debug("🔍 Identity not loaded, checking...");
|
||||
console.debug('🔍 Identity not loaded, checking...')
|
||||
|
||||
const userId = getUserId();
|
||||
const userId = getUserId()
|
||||
if (!userId) {
|
||||
console.debug("❌ No user ID available, redirecting to apply");
|
||||
console.groupEnd();
|
||||
next(PAGES.APPLY);
|
||||
return;
|
||||
console.debug('❌ No user ID available, redirecting to apply')
|
||||
console.groupEnd()
|
||||
next(PAGES.APPLY)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户身份
|
||||
await this.identityChecker.checkUserIdentity(userId);
|
||||
await this.identityChecker.checkUserIdentity(userId)
|
||||
}
|
||||
|
||||
// 4. 检查页面访问权限
|
||||
const hasPermission = permissionManager.hasPagePermission(to.path);
|
||||
const hasPermission = permissionManager.hasPagePermission(to.path)
|
||||
|
||||
if (hasPermission) {
|
||||
console.debug("✅ Permission granted, allowing access");
|
||||
console.groupEnd();
|
||||
next();
|
||||
console.debug('✅ Permission granted, allowing access')
|
||||
console.groupEnd()
|
||||
next()
|
||||
} else {
|
||||
console.debug("❌ Permission denied");
|
||||
const defaultPage = permissionManager.getDefaultPage();
|
||||
console.debug("🔄 Redirecting to default page:", defaultPage);
|
||||
console.groupEnd();
|
||||
next(defaultPage);
|
||||
console.debug('❌ Permission denied')
|
||||
const defaultPage = permissionManager.getDefaultPage()
|
||||
console.debug('🔄 Redirecting to default page:', defaultPage)
|
||||
console.groupEnd()
|
||||
next(defaultPage)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Route guard error:", error);
|
||||
console.groupEnd();
|
||||
console.error('❌ Route guard error:', error)
|
||||
console.groupEnd()
|
||||
|
||||
// 根据错误类型决定重定向目标
|
||||
if (error.message && error.message.includes("connection")) {
|
||||
next(PAGES.NOT_APP); // 连接相关错误
|
||||
if (error.message && error.message.includes('connection')) {
|
||||
next(PAGES.NOT_APP) // 连接相关错误
|
||||
} else {
|
||||
next(PAGES.APPLY); // 其他错误
|
||||
next(PAGES.APPLY) // 其他错误
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,15 +396,19 @@ class RouteGuard {
|
||||
*/
|
||||
isPublicPage(path) {
|
||||
const publicPages = [
|
||||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||||
'/404', // 404页面
|
||||
'/error', // 通用错误页面
|
||||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||||
'/404', // 404页面
|
||||
'/error', // 通用错误页面
|
||||
'/recharge',
|
||||
'/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) {
|
||||
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) {
|
||||
// 重定向到默认页面
|
||||
await this.pageRedirector.redirectToCorrectPage(router, currentPath, currentPath);
|
||||
await this.pageRedirector.redirectToCorrectPage(router, currentPath, currentPath)
|
||||
}
|
||||
|
||||
return primaryRole;
|
||||
return primaryRole
|
||||
} catch (error) {
|
||||
console.error("❌ Manual check and redirect failed:", error);
|
||||
throw error;
|
||||
console.error('❌ Manual check and redirect failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,17 +445,17 @@ class RouteGuard {
|
||||
* 重置守卫状态
|
||||
*/
|
||||
reset() {
|
||||
this.appConnectionChecker.reset();
|
||||
this.identityChecker.reset();
|
||||
permissionManager.reset();
|
||||
console.debug("🔄 Route guard reset");
|
||||
this.appConnectionChecker.reset()
|
||||
this.identityChecker.reset()
|
||||
permissionManager.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 checkAndRedirect = (router, userId) => routeGuard.checkAndRedirect(router, userId);
|
||||
export const resetRouteGuard = () => routeGuard.reset();
|
||||
export const installRouteGuard = (router) => routeGuard.install(router)
|
||||
export const checkAndRedirect = (router, userId) => routeGuard.checkAndRedirect(router, userId)
|
||||
export const resetRouteGuard = () => routeGuard.reset()
|
||||
|
||||
215
src/views/TopList/TopList.vue
Normal 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>
|
||||