收口前端平台组件库能力
新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件 迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome 补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { resolveRoleCombatStats } from '../data/attributeCombat';
|
||||
import { getAttributeSlotValue } from '../data/attributeResolver';
|
||||
import {
|
||||
type BuildContributionAttributeRow,
|
||||
type BuildDamageBreakdown,
|
||||
formatBuildContributionPercent,
|
||||
getBuildContributionQualityLabel,
|
||||
@@ -21,6 +22,10 @@ import {
|
||||
getSkillDeliveryLabel,
|
||||
getSkillStyleLabel,
|
||||
} from './CharacterInfoHelpers';
|
||||
import { PlatformEmptyState } from './common/PlatformEmptyState';
|
||||
import { PlatformPillBadge } from './common/PlatformPillBadge';
|
||||
import type { PlatformPillBadgeTone } from './common/platformPillBadgeModel';
|
||||
import { PlatformSubpanel } from './common/PlatformSubpanel';
|
||||
|
||||
export function StatusRow({
|
||||
label,
|
||||
@@ -68,28 +73,34 @@ export function CharacterIdentityBadges({
|
||||
roleTone?: 'amber' | 'sky' | 'rose' | 'emerald' | 'zinc';
|
||||
className?: string;
|
||||
}) {
|
||||
const roleClass =
|
||||
const roleBadgeTone: PlatformPillBadgeTone =
|
||||
roleTone === 'amber'
|
||||
? 'border-amber-300/20 bg-amber-500/10 text-amber-100'
|
||||
? 'darkAmber'
|
||||
: roleTone === 'rose'
|
||||
? 'border-rose-300/20 bg-rose-500/10 text-rose-100'
|
||||
? 'darkRose'
|
||||
: roleTone === 'emerald'
|
||||
? 'border-emerald-300/20 bg-emerald-500/10 text-emerald-100'
|
||||
? 'darkEmerald'
|
||||
: roleTone === 'zinc'
|
||||
? 'border-white/10 bg-black/20 text-zinc-200'
|
||||
: 'border-sky-300/20 bg-sky-500/10 text-sky-100';
|
||||
? 'darkNeutral'
|
||||
: 'darkSky';
|
||||
|
||||
return (
|
||||
<div className={`flex flex-wrap items-center gap-2 ${className}`.trim()}>
|
||||
<span
|
||||
className={`rounded-full border px-2.5 py-1 text-[10px] tracking-[0.16em] ${roleClass}`}
|
||||
<PlatformPillBadge
|
||||
tone={roleBadgeTone}
|
||||
size="xxs"
|
||||
className="tracking-[0.16em]"
|
||||
>
|
||||
{roleLabel}
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
{levelText ? (
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2.5 py-1 text-[10px] tracking-[0.16em] text-zinc-200">
|
||||
<PlatformPillBadge
|
||||
tone="darkNeutral"
|
||||
size="xxs"
|
||||
className="tracking-[0.16em]"
|
||||
>
|
||||
{levelText}
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
@@ -112,10 +123,7 @@ export function PlayerLevelProgress({
|
||||
const ratio =
|
||||
safeXpToNextLevel <= 0
|
||||
? 1
|
||||
: Math.max(
|
||||
0,
|
||||
Math.min(1, safeCurrentLevelXp / safeXpToNextLevel),
|
||||
);
|
||||
: Math.max(0, Math.min(1, safeCurrentLevelXp / safeXpToNextLevel));
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
@@ -150,9 +158,9 @@ export function CharacterSkillsList({
|
||||
}) {
|
||||
if (skills.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-white/5 bg-black/20 px-3 py-3 text-sm text-zinc-500">
|
||||
<PlatformEmptyState surface="editorDark" size="compact" tone="soft">
|
||||
{emptyText}
|
||||
</div>
|
||||
</PlatformEmptyState>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,9 +172,13 @@ export function CharacterSkillsList({
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="font-semibold text-white">{skill.name}</div>
|
||||
<span className="rounded-full border border-white/10 bg-white/6 px-2 py-0.5 text-[10px] text-zinc-100">
|
||||
<PlatformPillBadge
|
||||
tone="darkSoft"
|
||||
size="xxs"
|
||||
className="px-2 py-0.5"
|
||||
>
|
||||
{getSkillDeliveryLabel(skill)}
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-2 gap-2 text-[11px] text-zinc-400">
|
||||
<div>伤害:{skill.damage}</div>
|
||||
@@ -182,24 +194,32 @@ export function CharacterSkillsList({
|
||||
|
||||
if (onSelectSkill) {
|
||||
return (
|
||||
<button
|
||||
<PlatformSubpanel
|
||||
as="button"
|
||||
key={skillRenderId}
|
||||
type="button"
|
||||
onClick={() => onSelectSkill(skillRenderId)}
|
||||
className="rounded-xl border border-white/8 bg-black/20 px-3 py-3 text-left text-sm text-zinc-300 transition-colors hover:border-sky-300/25 hover:bg-sky-500/8"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
className="text-left text-sm text-zinc-300 transition-colors hover:border-sky-300/25 hover:bg-sky-500/8"
|
||||
>
|
||||
{content}
|
||||
</button>
|
||||
</PlatformSubpanel>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={skillRenderId}
|
||||
className="rounded-lg border border-white/5 bg-black/20 px-3 py-3 text-sm text-zinc-300"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
className="border-white/5 bg-black/20 text-sm text-zinc-300"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@@ -220,7 +240,13 @@ export function MultiplierContributionList({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-3 rounded-xl border border-sky-400/12 bg-sky-500/6 px-3 py-3">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="darkSky"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
className="space-y-3"
|
||||
>
|
||||
<div className="flex flex-col items-start gap-1 text-[10px] uppercase tracking-[0.16em] text-sky-100/80 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||
<span>状态标签</span>
|
||||
<span className="text-[9px] leading-4 text-zinc-400 sm:text-[10px]">
|
||||
@@ -251,10 +277,91 @@ export function MultiplierContributionList({
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-1 text-[10px] text-zinc-300">
|
||||
<PlatformPillBadge tone="darkNeutral" size="xxs" className="px-2">
|
||||
当前还没有形成有效标签
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
)}
|
||||
</PlatformSubpanel>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色构筑标签详情面板。
|
||||
* 统一承接队伍面板和实体详情弹窗里的标签概览、属性加成与空明细外壳。
|
||||
*/
|
||||
export function BuildContributionDetailPanel({
|
||||
row,
|
||||
attributes,
|
||||
emptyText = '当前标签还没有可展示的属性适配明细。',
|
||||
}: {
|
||||
row: ContributionRow;
|
||||
attributes: BuildContributionAttributeRow[];
|
||||
emptyText?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="grid gap-4 lg:grid-cols-[minmax(0,18rem)_minmax(0,1fr)]">
|
||||
<div className="space-y-4">
|
||||
<PlatformSubpanel
|
||||
surface="dark"
|
||||
radius="md"
|
||||
padding="md"
|
||||
className="px-4 py-4"
|
||||
style={getContributionVisualStyle(row.bonusDelta)}
|
||||
>
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[10px] uppercase tracking-[0.16em] text-current/70">
|
||||
标签概览
|
||||
</div>
|
||||
<div className="mt-2 text-sm font-semibold">{row.label}</div>
|
||||
</div>
|
||||
<div className="rounded-xl border border-current/15 bg-black/25 px-3 py-2 text-right">
|
||||
<div className="text-[11px] tracking-[0.14em] text-current/70">
|
||||
{getBuildContributionQualityLabel(row.bonusDelta)}
|
||||
</div>
|
||||
<div className="mt-1 text-sm font-semibold">
|
||||
总加成 {formatBuildContributionPercent(row.bonusDelta)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
</div>
|
||||
|
||||
<PlatformSubpanel surface="dark" radius="md" padding="md">
|
||||
<div className="text-[10px] uppercase tracking-[0.16em] text-zinc-500">
|
||||
属性加成
|
||||
</div>
|
||||
|
||||
{attributes.length > 0 ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{attributes.map((attribute) => (
|
||||
<PlatformSubpanel
|
||||
key={`${row.label}-${attribute.slotId}`}
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
className="px-4 py-3"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3 text-sm text-zinc-200">
|
||||
<span>{attribute.label}</span>
|
||||
<span className="font-semibold text-white">
|
||||
{formatBuildContributionPercent(attribute.modifierDelta)}
|
||||
</span>
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformSubpanel
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
className="mt-4 px-4 py-3 text-sm leading-6 text-zinc-400"
|
||||
>
|
||||
{emptyText}
|
||||
</PlatformSubpanel>
|
||||
)}
|
||||
</PlatformSubpanel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user