收口前端平台组件库能力
新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件 迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome 补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
@@ -7,9 +7,7 @@ import {
|
||||
} from '../data/attributeResolver';
|
||||
import {
|
||||
type BuildDamageBreakdown,
|
||||
formatBuildContributionPercent,
|
||||
getBuildContributionAttributeRows,
|
||||
getBuildContributionQualityLabel,
|
||||
getCompanionBuildDamageBreakdown,
|
||||
getPlayerBuildDamageBreakdown,
|
||||
} from '../data/buildDamage';
|
||||
@@ -51,10 +49,10 @@ import { BackstoryArchive } from './BackstoryArchive';
|
||||
import { CharacterAnimator } from './CharacterAnimator';
|
||||
import {
|
||||
getCharacterDetailSpriteStyle,
|
||||
getContributionVisualStyle,
|
||||
getGenderLabel,
|
||||
} from './CharacterInfoHelpers';
|
||||
import {
|
||||
BuildContributionDetailPanel,
|
||||
CharacterAttributeGrid,
|
||||
CharacterIdentityBadges,
|
||||
CharacterSkillsList,
|
||||
@@ -62,6 +60,8 @@ import {
|
||||
PlayerLevelProgress,
|
||||
StatusRow,
|
||||
} from './CharacterInfoShared';
|
||||
import { PlatformPillBadge } from './common/PlatformPillBadge';
|
||||
import { PlatformSubpanel } from './common/PlatformSubpanel';
|
||||
import type { GameCanvasEntitySelection } from './GameCanvas';
|
||||
import { MedievalNpcAnimator } from './MedievalNpcAnimator';
|
||||
import { PixelCloseButton } from './PixelCloseButton';
|
||||
@@ -417,16 +417,24 @@ export function CharacterPanel({
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-end gap-2 text-[11px] text-zinc-400">
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-zinc-200">
|
||||
<PlatformPillBadge
|
||||
tone="darkNeutral"
|
||||
size="xs"
|
||||
className="px-2 py-0.5 font-normal text-zinc-200"
|
||||
>
|
||||
{buildBreakdownByMemberId[member.id]?.baseTagCount ?? 0}{' '}
|
||||
标签
|
||||
</span>
|
||||
<span className="rounded-full border border-emerald-400/20 bg-emerald-500/10 px-2 py-0.5 text-emerald-100">
|
||||
</PlatformPillBadge>
|
||||
<PlatformPillBadge
|
||||
tone="darkEmerald"
|
||||
size="xs"
|
||||
className="px-2 py-0.5 font-normal"
|
||||
>
|
||||
{'\u9002\u914d'} x
|
||||
{buildBreakdownByMemberId[
|
||||
member.id
|
||||
]?.buildDamageMultiplier.toFixed(2) ?? '1.00'}
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -473,72 +481,10 @@ export function CharacterPanel({
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto p-4 sm:p-5">
|
||||
<div className="grid gap-4 lg:grid-cols-[minmax(0,18rem)_minmax(0,1fr)]">
|
||||
<div className="space-y-4">
|
||||
<div
|
||||
className="rounded-2xl border px-4 py-4"
|
||||
style={getContributionVisualStyle(
|
||||
selectedContributionRow.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">
|
||||
{selectedContributionRow.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(
|
||||
selectedContributionRow.bonusDelta,
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 text-sm font-semibold">
|
||||
{'\u603b\u52a0\u6210'}{' '}
|
||||
{formatBuildContributionPercent(
|
||||
selectedContributionRow.bonusDelta,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-white/8 bg-black/20 p-4">
|
||||
<div className="text-[10px] uppercase tracking-[0.16em] text-zinc-500">
|
||||
{'\u5c5e\u6027\u52a0\u6210'}
|
||||
</div>
|
||||
|
||||
{selectedContributionAttributes.length > 0 ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{selectedContributionAttributes.map((attribute) => (
|
||||
<div
|
||||
key={`${selectedContributionRow.label}-${attribute.slotId}`}
|
||||
className="rounded-xl border border-white/8 bg-black/25 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>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-4 rounded-xl border border-white/8 bg-black/25 px-4 py-3 text-sm leading-6 text-zinc-400">
|
||||
{
|
||||
'\u5f53\u524d\u6807\u7b7e\u8fd8\u6ca1\u6709\u53ef\u5c55\u793a\u7684\u5c5e\u6027\u9002\u914d\u660e\u7ec6\u3002'
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<BuildContributionDetailPanel
|
||||
row={selectedContributionRow}
|
||||
attributes={selectedContributionAttributes}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
@@ -580,9 +526,13 @@ export function CharacterPanel({
|
||||
levelText={selectedMember.levelText}
|
||||
roleTone={selectedMember.isLeader ? 'amber' : 'sky'}
|
||||
/>
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-[9px] text-zinc-200">
|
||||
<PlatformPillBadge
|
||||
tone="darkNeutral"
|
||||
size="xxs"
|
||||
className="px-2 py-0.5 text-[9px] font-normal text-zinc-200"
|
||||
>
|
||||
{getGenderLabel(selectedMember.character.gender)}
|
||||
</span>
|
||||
</PlatformPillBadge>
|
||||
</div>
|
||||
</div>
|
||||
<PixelCloseButton
|
||||
@@ -639,7 +589,13 @@ export function CharacterPanel({
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{selectedMember.isLeader && (
|
||||
<div className="rounded-xl border border-amber-300/18 bg-amber-500/8 px-3 py-3">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="darkAmber"
|
||||
radius="xs"
|
||||
padding="sm"
|
||||
data-testid="character-panel-level-progress"
|
||||
>
|
||||
<div className="mb-2 text-[10px] uppercase tracking-[0.18em] text-amber-100/75">
|
||||
等级
|
||||
</div>
|
||||
@@ -652,7 +608,7 @@ export function CharacterPanel({
|
||||
normalizedPlayerProgression.xpToNextLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
)}
|
||||
<StatusRow
|
||||
label={resourceLabels.hp}
|
||||
@@ -670,7 +626,13 @@ export function CharacterPanel({
|
||||
<AffinityStatusCard affinity={selectedMemberAffinity} />
|
||||
)}
|
||||
{selectedMemberArcState && (
|
||||
<div className="rounded-xl border border-white/8 bg-black/20 px-3 py-2 text-xs text-zinc-300">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="row"
|
||||
className="text-xs text-zinc-300"
|
||||
>
|
||||
<div className="text-[10px] uppercase tracking-[0.18em] text-zinc-500">
|
||||
个人线阶段
|
||||
</div>
|
||||
@@ -680,10 +642,17 @@ export function CharacterPanel({
|
||||
<div className="mt-1 text-[11px] text-sky-200/85">
|
||||
{selectedMemberArcState.arcTheme}
|
||||
</div>
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
)}
|
||||
{selectedMemberResolution && (
|
||||
<div className="rounded-xl border border-emerald-400/18 bg-emerald-500/8 px-3 py-2 text-xs text-zinc-300">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="darkEmerald"
|
||||
radius="xs"
|
||||
padding="row"
|
||||
className="text-xs"
|
||||
data-testid="character-panel-resolution"
|
||||
>
|
||||
<div className="text-[10px] uppercase tracking-[0.18em] text-emerald-200/80">
|
||||
收束状态
|
||||
</div>
|
||||
@@ -693,7 +662,7 @@ export function CharacterPanel({
|
||||
<div className="mt-1 text-[11px] text-emerald-100/85">
|
||||
{selectedMemberResolution.summary}
|
||||
</div>
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
)}
|
||||
{selectedMemberAffinity != null && (
|
||||
<BackstoryArchive
|
||||
@@ -735,9 +704,15 @@ export function CharacterPanel({
|
||||
<div className="mb-3 text-xs font-bold text-white">
|
||||
背景故事
|
||||
</div>
|
||||
<div className="rounded-xl border border-white/8 bg-black/18 px-4 py-3 text-sm leading-relaxed text-zinc-300">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="md"
|
||||
className="text-sm leading-relaxed text-zinc-300"
|
||||
>
|
||||
{selectedMember.character.backstory}
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -748,9 +723,15 @@ export function CharacterPanel({
|
||||
<div className="mb-3 text-xs font-bold text-white">
|
||||
性格
|
||||
</div>
|
||||
<div className="rounded-xl border border-white/8 bg-black/18 px-4 py-3 text-sm leading-relaxed text-zinc-300">
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="md"
|
||||
className="text-sm leading-relaxed text-zinc-300"
|
||||
>
|
||||
{selectedMember.character.personality}
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -774,9 +755,13 @@ export function CharacterPanel({
|
||||
</div>
|
||||
<div className="space-y-2 text-sm text-zinc-300">
|
||||
{selectedEquipmentRows.map((item) => (
|
||||
<div
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={item.key}
|
||||
className="flex items-center justify-between rounded-lg border border-white/5 bg-black/20 px-3 py-2"
|
||||
surface="dark"
|
||||
radius="xs"
|
||||
padding="row"
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<PixelIcon
|
||||
@@ -790,10 +775,14 @@ export function CharacterPanel({
|
||||
<div>{item.itemLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className="rounded-full border border-amber-500/20 bg-amber-500/10 px-2 py-0.5 text-[10px] text-amber-100">
|
||||
<PlatformPillBadge
|
||||
tone="darkAmber"
|
||||
size="xxs"
|
||||
className="px-2 py-0.5 font-normal"
|
||||
>
|
||||
{item.rarityLabel}
|
||||
</span>
|
||||
</div>
|
||||
</PlatformPillBadge>
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user