123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- /* eslint-disable */
- import React from 'react';
- import { resolveIcon, ResolvedIcon } from './iconRegistry';
- // 为了在 useAntdIcon=true 时使用 antd 的 Icon,我们做一个条件导入
- // 注意:如果项目没安装 @ant-design/icons,也不会报错,只是返回 undefined
- type AntdIconType = React.ComponentType<{
- component: React.ComponentType;
- className?: string;
- style?: React.CSSProperties;
- }>;
- function tryLoadAntdIcon(): AntdIconType | undefined {
- try {
- const mod = require('@ant-design/icons') as { default?: AntdIconType };
- return mod?.default;
- } catch {
- return undefined;
- }
- }
- export interface IconProps extends React.ImgHTMLAttributes<HTMLImageElement> {
- userId?: string;
- productId?: string; // 新增:产品ID
- module: string;
- name: string; // e.g. 'icon-nav-back' (不带状态与后缀)
- theme?: string;
- size?: '1x' | '2x' | '3x';
- state?: string;
- alt?: string;
- useAntdIcon?: boolean; // 是否尝试用 @ant-design/icons 包裹 svg
- widthPx?: number; // 以像素指定大小(覆盖 class 控制)
- url?: string; // 新增:直接指定图标URL,优先级高于其他图标解析参数
- }
- /**
- * 自定义 Icon 组件
- */
- const Icon: React.FC<IconProps> = ({
- userId,
- productId,
- module,
- name,
- theme = 'default',
- size = '2x',
- state = 'default',
- alt,
- useAntdIcon = true,
- widthPx,
- className,
- style,
- url, // 新增:解构url属性
- ...imgProps
- }) => {
- // 新增:优先处理URL图标
- if (url) {
- // 类型安全:确保url是字符串
- if (typeof url !== 'string') {
- console.warn('Icon组件: url属性必须是字符串类型');
- return null;
- }
- // 基本URL格式验证
- try {
- new URL(url); // 这会验证URL格式,但不要求必须是http/https
- } catch (e) {
- // 如果不是有效的URL,可能是相对路径,这是允许的
- if (!url.startsWith('/') && !url.startsWith('./') && !url.startsWith('../')) {
- console.warn('Icon组件: url属性格式无效', url);
- }
- }
- return (
- <img
- src={url}
- alt={alt ?? name}
- width={widthPx ?? undefined}
- height={widthPx ?? undefined}
- className={className}
- style={style}
- // 添加错误处理
- onError={(e) => {
- console.warn('Icon组件: URL图标加载失败', url);
- // 调用用户提供的onError回调(如果存在)
- if (imgProps.onError) {
- imgProps.onError(e);
- }
- }}
- {...imgProps}
- />
- );
- }
- const resolved: ResolvedIcon | undefined = resolveIcon({
- userId,
- productId,
- module,
- name,
- theme,
- size,
- state,
- });
- if (!resolved) {
- // 未找到图标,可以返回 null 或者 fallback
- return null;
- }
- // if (resolved.kind === 'svg') {
- // console.log(`解析到了svg图标`);
- // const Svg = resolved.Component;
- // console.log(`打印一下 resolved.Component 看是什么 === ${Svg}`)
- // if (useAntdIcon) {
- // const AntdIcon = tryLoadAntdIcon();
- // if (AntdIcon) {
- // return (
- // <AntdIcon
- // component={Svg}
- // className={className}
- // style={{
- // width: widthPx ?? undefined,
- // height: widthPx ?? undefined,
- // ...style,
- // }}
- // />
- // );
- // }
- // }
- // // 没有 antd 或 useAntdIcon=false,则直接渲染 svg
- // return (
- // <Svg
- // width={widthPx ?? undefined}
- // height={widthPx ?? undefined}
- // role={alt ? 'img' : 'presentation'}
- // aria-label={alt}
- // className={className}
- // style={style}
- // />
- // );
- // }
- if (resolved.kind === 'svg') {
- const Svg = resolved.Component;
- //运行时保护 — 若 resolved.Component 非函数/非组件(意外情况),尝试降级为 <img />
- if (Svg == null || typeof Svg !== 'function') {
- // 尝试从 meta.source 拿到可用的 URL(可能是 svg 被打包为 URL 的情况)
- const maybeSrc = (resolved.meta && (resolved.meta.source as unknown)) ?? null;
- if (typeof maybeSrc === 'string') {
- return (
- <img
- src={maybeSrc}
- alt={alt ?? name}
- width={widthPx ?? undefined}
- height={widthPx ?? undefined}
- className={className}
- style={style}
- {...imgProps}
- />
- );
- }
- // 无法降级则安全返回 null
- return null;
- }
- if (useAntdIcon) {
- const AntdIcon = tryLoadAntdIcon();
- if (AntdIcon) {
- return (
- <AntdIcon
- component={Svg}
- className={className}
- style={{
- width: widthPx ?? undefined,
- height: widthPx ?? undefined,
- ...style,
- }}
- />
- );
- }
- }
- // 直接渲染 svg 组件
- return (
- <Svg
- width={widthPx ?? undefined}
- height={widthPx ?? undefined}
- role={alt ? 'img' : 'presentation'}
- aria-label={alt}
- className={className}
- style={style}
- />
- );
- }
- // raster 图片
- return (
- <img
- src={resolved.src}
- srcSet={resolved.srcSet}
- alt={alt ?? name}
- width={widthPx ?? undefined}
- height={widthPx ?? undefined}
- className={className}
- style={style}
- {...imgProps}
- />
- );
- };
- export default Icon;
|