将"企业微信"导航项从 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>
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
// ============================================================
|
||
// WechatWorkPage — 企业微信二维码 Modal 内容
|
||
//
|
||
// 点击导航栏"企业微信"后以 Modal 形式展示二维码。
|
||
// 数据来源:GET /api/v1/creatives(Banner API)
|
||
//
|
||
// 识别逻辑:Banner 中 category === 'promotion' 的条目,
|
||
// 其 image_url 为企业微信二维码图片
|
||
// ============================================================
|
||
|
||
import { useEffect, useState, useCallback } from 'react';
|
||
import { Spin, Empty } from 'antd';
|
||
import { WechatOutlined } from '@ant-design/icons';
|
||
import { fetchBanners, type Banner } from '@/services/modules';
|
||
|
||
export function WechatWorkPage() {
|
||
const [banner, setBanner] = useState<Banner | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
const load = useCallback(async () => {
|
||
setLoading(true);
|
||
setError(null);
|
||
try {
|
||
const list = await fetchBanners();
|
||
// category === 'promotion' → 企业微信二维码
|
||
const promotion = list.find(
|
||
(item) => item.is_active && item.category === 'promotion',
|
||
);
|
||
setBanner(promotion ?? null);
|
||
} catch (err) {
|
||
setError((err as Error).message || '加载失败');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, []);
|
||
useEffect(() => {
|
||
void load();
|
||
}, [load]);
|
||
|
||
// 加载态
|
||
if (loading) {
|
||
return (
|
||
<div style={{ display: 'flex', justifyContent: 'center', padding: 48 }}>
|
||
<Spin description="加载中..." />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 无数据 / 错误
|
||
if (error || !banner || !banner.image_url) {
|
||
return (
|
||
<div style={{ display: 'flex', justifyContent: 'center', padding: 48 }}>
|
||
<Empty
|
||
image={<WechatOutlined style={{ fontSize: 48, color: '#9CA3AF' }} />}
|
||
description={error || '暂未获取到企业微信二维码,请联系管理员'}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16, padding: 8 }}>
|
||
{/* 二维码图片 */}
|
||
<div
|
||
style={{
|
||
width: 240,
|
||
height: 240,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
borderRadius: 8,
|
||
background: '#fff',
|
||
border: '1px solid #E5E7EB',
|
||
overflow: 'hidden',
|
||
}}
|
||
>
|
||
<img
|
||
src={banner.image_url}
|
||
alt="企业微信二维码"
|
||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||
/>
|
||
</div>
|
||
|
||
{/* 标题(如果 Banner 有标题则显示) */}
|
||
{banner.title && (
|
||
<p style={{ margin: 0, fontSize: 14, fontWeight: 500, color: 'inherit' }}>
|
||
{banner.title}
|
||
</p>
|
||
)}
|
||
|
||
{/* 提示文字 */}
|
||
<p style={{ margin: 0, fontSize: 13, color: 'inherit', opacity: 0.5 }}>
|
||
请使用微信扫描二维码添加企业微信
|
||
</p>
|
||
</div>
|
||
);
|
||
}
|