PageDispatcher 重构: - singleton + floatingPanels[] 双状态 → 单一 activePanel 状态 - 全局互斥:已打开面板时禁止打开新面板(保留拖拽/图片拖放等互操作) - 占位组件预计算为模块级 Map,消除 ESLint static-components 误报 - PAGE_MAP 支持 width/height 配置,动态函数计算(视口比例自适应) - 容器默认尺寸改为动态:Modal=min(w*0.55,720) Drawer=min(w*0.38,520) - 修复 FloatingPanel 未接收 width/height 配置的问题 Test2 账户信息页: - AuthAPI.getUserInfo() → 三卡片布局(积分/套餐/今日统计) - 加载态 Skeleton / 错误态 Result+重试 - centToYuan 积分转换、formatDate 日期格式化 目录调整:wechat-work/ → headers/ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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/banner';
|
||
|
||
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>
|
||
);
|
||
}
|