52 lines
2.1 KiB
JavaScript
52 lines
2.1 KiB
JavaScript
import { useState } from "react";
|
||
import styles from "./AppIdentity.module.css";
|
||
|
||
// AppIdentity 是后台中 App 的唯一展示形式:业务页只传目录数据,避免各个下拉、表格和弹窗分别维护 Logo 规则。
|
||
export function AppIdentity({ app, appCode, appName, className = "", logoUrl, showLogoFallback = true, size = "medium" }) {
|
||
// 主后台使用 camelCase,Social BI 的服务端聚合契约使用 snake_case;组件在展示边界统一兼容,业务视图不复制字段转换。
|
||
const resolvedCode = String(app?.appCode || app?.app_code || appCode || "").trim();
|
||
const resolvedName = String(app?.appName || app?.app_name || appName || resolvedCode || "-").trim();
|
||
const resolvedLogoUrl = String(app?.logoUrl || app?.logo_url || logoUrl || "").trim();
|
||
|
||
return (
|
||
<span className={[styles.root, styles[size] || styles.medium, className].filter(Boolean).join(" ")}>
|
||
<AppLogo appCode={resolvedCode} appName={resolvedName} logoUrl={resolvedLogoUrl} showFallback={showLogoFallback} />
|
||
<span className={styles.name}>{resolvedName}</span>
|
||
</span>
|
||
);
|
||
}
|
||
|
||
function AppLogo({ appCode, appName, logoUrl, showFallback }) {
|
||
const [failedLogoUrl, setFailedLogoUrl] = useState("");
|
||
const canRenderImage = Boolean(logoUrl) && failedLogoUrl !== logoUrl;
|
||
|
||
if (canRenderImage) {
|
||
return (
|
||
<img
|
||
alt=""
|
||
className={styles.logo}
|
||
loading="lazy"
|
||
src={logoUrl}
|
||
onError={() => setFailedLogoUrl(logoUrl)}
|
||
/>
|
||
);
|
||
}
|
||
|
||
if (!showFallback) {
|
||
// 外管品牌只能展示 apps.logo_url 的真实图片;缺失或加载失败时不伪造一个看似品牌 Logo 的首字母图标。
|
||
return null;
|
||
}
|
||
|
||
// 网络失败或历史数据暂无 Logo 时保留稳定占位,不让选择项高度和表格对齐抖动。
|
||
const initial =
|
||
String(appName || appCode || "?")
|
||
.trim()
|
||
.slice(0, 1)
|
||
.toUpperCase() || "?";
|
||
return (
|
||
<span aria-hidden="true" className={styles.fallback}>
|
||
{initial}
|
||
</span>
|
||
);
|
||
}
|