message / frontend /src /components /console /MetadataList.tsx
hunian
feat: 重构前端控制台和工具页面
a43ac26
Raw
History Blame Contribute Delete
2.84 kB
import React from 'react';
import { cn } from '@/lib/utils';
/**
* 控制台元数据列表组件
* 用于展示键值对形式的元数据信息
*/
interface MetadataItem {
/** 键名 */
key: string;
/** 值 */
value: React.ReactNode;
/** 是否为可选字段 */
optional?: boolean;
}
interface MetadataListProps {
/** 元数据列表 */
items: MetadataItem[];
/** 布局方向 */
layout?: 'vertical' | 'horizontal';
/** 额外的 className */
className?: string;
}
/**
* 元数据列表组件
* 用于控制台中展示键值对信息
*/
export function MetadataList({
items,
layout = 'vertical',
className,
}: MetadataListProps) {
// 过滤掉空值
const visibleItems = items.filter(
(item) => item.value !== null && item.value !== undefined && item.value !== ''
);
if (visibleItems.length === 0) {
return (
<div className={cn('py-2 text-xs text-console-text-muted', className)}>
暂无元数据
</div>
);
}
if (layout === 'horizontal') {
return (
<div className={cn('flex flex-wrap gap-x-4 gap-y-1', className)}>
{visibleItems.map((item) => (
<div key={item.key} className="flex items-center gap-1.5 text-xs">
<span className="text-console-text-secondary">{item.key}:</span>
<span className="text-console-text-primary">{item.value}</span>
</div>
))}
</div>
);
}
return (
<dl className={cn('space-y-1', className)}>
{visibleItems.map((item) => (
<div key={item.key} className="flex items-start gap-2 text-xs">
<dt className="w-24 shrink-0 text-console-text-secondary">
{item.key}
{item.optional && (
<span className="ml-1 text-console-text-muted">(可选)</span>
)}
</dt>
<dd className="flex-1 min-w-0 text-console-text-primary">
{item.value}
</dd>
</div>
))}
</dl>
);
}
/**
* 元数据项组件
* 单个键值对展示
*/
interface MetadataEntryProps {
/** 键名 */
label: string;
/** 值 */
value: React.ReactNode;
/** 是否为可选字段 */
optional?: boolean;
/** 额外的 className */
className?: string;
}
export function MetadataEntry({
label,
value,
optional,
className,
}: MetadataEntryProps) {
if (value === null || value === undefined || value === '') {
return null;
}
return (
<div className={cn('flex items-start gap-2 text-xs', className)}>
<span className="w-24 shrink-0 text-console-text-secondary">
{label}
{optional && (
<span className="ml-1 text-console-text-muted">(可选)</span>
)}
</span>
<span className="flex-1 min-w-0 text-console-text-primary">
{value}
</span>
</div>
);
}