feat: 企业微信改为 Modal 展示二维码,修复 PageDispatcher hooks 错误
将"企业微信"导航项从 external(跳转浏览器)改为 page(Modal 展示),
二维码图片数据来源于 Banner API(/api/v1/creatives,category=promotion)。
- 新增 WechatWorkPage:调用 fetchBanners,筛选 category==='promotion',
展示 image_url 二维码 + 扫码提示 + 刷新按钮,加载/空态/错误三态覆盖
- nav-config:wechat-work 从 action:'external' 改为 action:'page',
modalType:'modal',target:'/wechat-work'
- PageDispatcher:注册 /wechat-work 映射,Modal 新增 centered 居中
- 修复 PageDispatcher 致命 bug:PAGE_MAP[...]() 函数调用改为
JSX <SingletonPage />,避免子组件 hooks 误跑在父组件链上导致崩溃
- PAGE_MAP 类型从 Record<string, () => ReactNode> 修正为
Record<string, ComponentType>
This commit is contained in:
@@ -9,18 +9,19 @@
|
||||
// 支持多面板共存(floating 类型可同时打开多个)
|
||||
// ============================================================
|
||||
|
||||
import { useState, useEffect, useCallback, useRef, type ReactNode } from 'react';
|
||||
import { Modal, Drawer } from 'antd';
|
||||
import { on, off, EVENTS } from '@/utils/event-bus';
|
||||
import { FloatingPanel } from '@/components/common/FloatingPanel';
|
||||
import { ComplianceAssetsPage } from '@/pages/compliance-assets/ComplianceAssetsPage';
|
||||
import { AccountPage } from '@/pages/account/AccountPage';
|
||||
import { VipPage } from '@/pages/vip/VipPage';
|
||||
import { RechargePage } from '@/pages/recharge/RechargePage';
|
||||
import { BillingPage } from '@/pages/billing/BillingPage';
|
||||
import { OrdersPage } from '@/pages/orders/OrdersPage';
|
||||
import { ProjectsPage } from '@/pages/projects/ProjectsPage';
|
||||
import { QuotaPage } from '@/pages/quota/QuotaPage';
|
||||
import {useState, useEffect, useCallback, useRef, type ComponentType} from 'react';
|
||||
import {Modal, Drawer} from 'antd';
|
||||
import {on, off, EVENTS} from '@/utils/event-bus';
|
||||
import {FloatingPanel} from '@/components/common/FloatingPanel';
|
||||
import {ComplianceAssetsPage} from '@/pages/compliance-assets/ComplianceAssetsPage';
|
||||
import {AccountPage} from '@/pages/account/AccountPage';
|
||||
import {VipPage} from '@/pages/vip/VipPage';
|
||||
import {RechargePage} from '@/pages/recharge/RechargePage';
|
||||
import {BillingPage} from '@/pages/billing/BillingPage';
|
||||
import {OrdersPage} from '@/pages/orders/OrdersPage';
|
||||
import {ProjectsPage} from '@/pages/projects/ProjectsPage';
|
||||
import {QuotaPage} from '@/pages/quota/QuotaPage';
|
||||
import {WechatWorkPage} from '@/pages/wechat-work/WechatWorkPage';
|
||||
|
||||
// ---------- 类型 ----------
|
||||
|
||||
@@ -40,7 +41,7 @@ interface ActivePanel {
|
||||
|
||||
// ---------- target → 页面组件映射 ----------
|
||||
|
||||
const PAGE_MAP: Record<string, () => ReactNode> = {
|
||||
const PAGE_MAP: Record<string, ComponentType> = {
|
||||
'/compliance-assets': ComplianceAssetsPage,
|
||||
'/account': AccountPage,
|
||||
'/vip': VipPage,
|
||||
@@ -49,6 +50,7 @@ const PAGE_MAP: Record<string, () => ReactNode> = {
|
||||
'/orders': OrdersPage,
|
||||
'/projects': ProjectsPage,
|
||||
'/quota': QuotaPage,
|
||||
'/wechat-work': WechatWorkPage,
|
||||
};
|
||||
|
||||
// ---------- 组件 ----------
|
||||
@@ -64,7 +66,7 @@ export function PageDispatcher() {
|
||||
// 监听 OPEN_PAGE 事件
|
||||
useEffect(() => {
|
||||
const handler = (payload: unknown) => {
|
||||
const { target, modalType, label } = payload as PageEventPayload;
|
||||
const {target, modalType, label} = payload as PageEventPayload;
|
||||
|
||||
if (!target || !PAGE_MAP[target]) {
|
||||
console.warn(`[PageDispatcher] 未找到页面组件: ${target}`);
|
||||
@@ -74,10 +76,10 @@ export function PageDispatcher() {
|
||||
if (modalType === 'floating') {
|
||||
// 浮动面板:追加到列表
|
||||
const id = `fp-${nextIdRef.current++}`;
|
||||
setFloatingPanels((prev) => [...prev, { id, target, label }]);
|
||||
setFloatingPanels((prev) => [...prev, {id, target, label}]);
|
||||
} else {
|
||||
// modal / drawer:单实例,覆盖之前的
|
||||
setSingleton({ target, modalType, label });
|
||||
setSingleton({target, modalType, label});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,36 +97,39 @@ export function PageDispatcher() {
|
||||
setFloatingPanels((prev) => prev.filter((p) => p.id !== id));
|
||||
}, []);
|
||||
|
||||
// 渲染单实例内容
|
||||
const singletonContent = singleton && PAGE_MAP[singleton.target] ? PAGE_MAP[singleton.target]() : null;
|
||||
// 单实例对应的页面组件
|
||||
const SingletonPage = singleton ? PAGE_MAP[singleton.target] : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ======== Modal ======== */}
|
||||
{singleton?.modalType === 'modal' && (
|
||||
{singleton?.modalType === 'modal' && SingletonPage && (
|
||||
<Modal
|
||||
open
|
||||
centered
|
||||
title={singleton.label}
|
||||
onCancel={closeSingleton}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
maskClosable
|
||||
destroyOnHidden={true}
|
||||
mask={{
|
||||
closable: true,
|
||||
}}
|
||||
width={640}
|
||||
>
|
||||
{singletonContent}
|
||||
<SingletonPage />
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
{/* ======== Drawer ======== */}
|
||||
{singleton?.modalType === 'drawer' && (
|
||||
{singleton?.modalType === 'drawer' && SingletonPage && (
|
||||
<Drawer
|
||||
open
|
||||
title={singleton.label}
|
||||
onClose={closeSingleton}
|
||||
destroyOnClose
|
||||
width={480}
|
||||
destroyOnHidden={true}
|
||||
size={480}
|
||||
>
|
||||
{singletonContent}
|
||||
<SingletonPage />
|
||||
</Drawer>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user