收口个人中心已玩弹窗组件
迁移个人中心存档和玩过弹窗简单空态到 PlatformEmptyState 迁移玩过弹窗分区标题到 PlatformFieldLabel 迁移已玩作品按钮卡到 PlatformSubpanel 并保留粉色 hover 边框 补充已玩弹窗公共组件断言并更新 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
@@ -65,6 +65,7 @@
|
||||
- 2026-06-10 追加:个人中心兑换码和邀请兑换输入使用 `PlatformTextField surface="platform"`;业务组件只保留兑换 / 邀请码提交、归一化、大写展示、Enter 提交和原生可访问名称,不再手写 `platform-profile-input` 或白底 input chrome。
|
||||
- 2026-06-09 追加:平台字段标签统一使用 `src/components/common/PlatformFieldLabel.tsx` 承载 `field`、`section`、`form`、`pill` 与 `accentPill` 五类字段标题视觉;视觉小说结果页、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移,业务页只保留字段文案和必要局部布局 class,不再重复拼普通字段名、分区标题、表单标题、普通胶囊和强调胶囊 class。
|
||||
- 2026-06-10 追加:通用创作图片输入面板的主图标题和提示词标题使用 `PlatformFieldLabel variant="form"`;提示词字段保留外层原生 `label htmlFor`,业务组件只保留字段文案、布局和上传 / 生成交互,不再手写 `mb-2 block text-sm font-black` 标题 class。
|
||||
- 2026-06-10 追加:个人中心存档 / 玩过弹窗里的简单空态使用 `PlatformEmptyState surface="subpanel" size="inline"`,玩过弹窗的“可继续 / 玩过”分区标题使用 `PlatformFieldLabel variant="section"`,已玩作品白底按钮卡使用 `PlatformSubpanel as="button" surface="flat" radius="sm" padding="md" interactive`;`SaveArchiveCard` 因含图片遮罩和加载态暂不并入本轮。
|
||||
- 2026-06-09 追加:抓大鹅结果页作品信息、发布封面和物品素材详情中的 section 字段标题迁移到 `PlatformFieldLabel variant="section"`;业务页不再重复拼 `text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`。
|
||||
- 2026-06-09 追加:方洞结果页主信息、形状选项、洞口选项和历史生成标题迁移到 `PlatformFieldLabel variant="section"`;业务页只保留字段文案、图标和按钮布局,不再重复拼 section 标题 class。
|
||||
- 2026-06-09 追加:拼图结果页关卡详情的“关卡名称”和发布弹窗的“发布检查 / 封面关卡”标题迁移到 `PlatformFieldLabel variant="section"`;业务页保留 label 关联和弹窗布局,不再重复拼 section 标题 class。
|
||||
@@ -250,6 +251,7 @@
|
||||
- 2026-06-10 验证补充:认证图形验证码图片和答案输入分别收口到 `PlatformMediaFrame` 与 `PlatformTextField` 后,补跑 `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
|
||||
- 2026-06-10 验证补充:认证登录、重置密码、绑定手机号、邀请码和账号安全表单字段收口到 `PlatformTextField` 与 `PlatformFieldLabel` 后,补跑 `npm run test -- src/components/auth/AuthGate.test.tsx src/components/auth/AccountModal.test.tsx src/components/auth/BindPhoneScreen.test.tsx src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
|
||||
- 2026-06-10 验证补充:通用创作图片输入面板主图 / 提示词字段标题收口到 `PlatformFieldLabel` 后,补跑 `npm run test -- src/components/common/CreativeImageInputPanel.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
|
||||
- 2026-06-10 验证补充:个人中心存档 / 玩过弹窗简单空态、分区标题和已玩作品按钮卡收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "profile played modal|profile page keeps save archives inside played stats panel"`。
|
||||
- 2026-06-10 验证补充:个人中心兑换码 / 邀请码输入和充值 / 任务空态收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformEmptyState.test.tsx -t "reward code|invite query|profile redeem invite|daily task"`。
|
||||
- 2026-06-10 验证补充:背包文书按钮收口到暗色 `PlatformSubpanel`、故事档案 QA 提示收口到 `PlatformStatusMessage surface="editorDark"` 后,补跑 `npm run test -- src/components/InventoryPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
||||
- 2026-06-10 验证补充:NPC 叙事提示和交易详情属性格收口后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11646,6 +11646,11 @@ test('profile page keeps save archives inside played stats panel', async () => {
|
||||
|
||||
await openProfilePlayedWorks(user);
|
||||
|
||||
const continueSectionLabel = screen.getByText('可继续');
|
||||
expect(continueSectionLabel.className).toContain('tracking-[0.18em]');
|
||||
expect(continueSectionLabel.className).toContain(
|
||||
'text-[var(--platform-text-soft)]',
|
||||
);
|
||||
expect(screen.queryByLabelText('关闭存档')).toBeNull();
|
||||
expect(screen.queryByText('SAVES')).toBeNull();
|
||||
await clickFirstAsyncButtonByName(user, /潮雾列岛/u);
|
||||
|
||||
@@ -2759,6 +2759,22 @@ test('profile played modal summary and work type use platform pill badges', asyn
|
||||
const playedModal = screen
|
||||
.getByText('潮雾列岛')
|
||||
.closest('.fixed') as HTMLElement;
|
||||
const playedSectionLabel = within(playedModal)
|
||||
.getAllByText('玩过')
|
||||
.find((element) => element.className.includes('tracking-[0.18em]'));
|
||||
expect(playedSectionLabel?.className).toContain(
|
||||
'text-[var(--platform-text-soft)]',
|
||||
);
|
||||
|
||||
const playedWorkButton = within(playedModal)
|
||||
.getByText('潮雾列岛')
|
||||
.closest('button') as HTMLElement;
|
||||
expect(playedWorkButton.className).toContain(
|
||||
'border-[var(--platform-subpanel-border)]',
|
||||
);
|
||||
expect(playedWorkButton.className).toContain('hover:border-[#ff4056]');
|
||||
expect(playedWorkButton.className).toContain('focus-visible:ring-2');
|
||||
|
||||
const totalPlayTimeBadge = within(playedModal).getByText('1.5小时');
|
||||
expect(totalPlayTimeBadge.className).toContain('rounded-full');
|
||||
expect(totalPlayTimeBadge.className).toContain('border-rose-100');
|
||||
@@ -2770,6 +2786,32 @@ test('profile played modal summary and work type use platform pill badges', asyn
|
||||
expect(workTypeBadge.className).toContain('text-[#ff4056]');
|
||||
});
|
||||
|
||||
test('profile played modal empty state uses platform empty state', async () => {
|
||||
renderProfileView(
|
||||
vi.fn(),
|
||||
{
|
||||
playedWorldCount: 0,
|
||||
},
|
||||
{},
|
||||
0,
|
||||
{
|
||||
isProfilePlayStatsOpen: true,
|
||||
profilePlayStats: {
|
||||
totalPlayTimeMs: 0,
|
||||
playedWorks: [],
|
||||
updatedAt: '2026-04-19T12:00:00.000Z',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const emptyState = await screen.findByText('暂无玩过');
|
||||
expect(emptyState.className).toContain('platform-empty-state');
|
||||
expect(emptyState.className).toContain(
|
||||
'border-[var(--platform-subpanel-border)]',
|
||||
);
|
||||
expect(emptyState.className).toContain('text-left');
|
||||
});
|
||||
|
||||
test('profile played works card shows count unit', async () => {
|
||||
renderProfileView(vi.fn(), {
|
||||
playedWorldCount: 1,
|
||||
|
||||
@@ -122,6 +122,7 @@ import {
|
||||
} from '../common/legalDocuments';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformEmptyState } from '../common/PlatformEmptyState';
|
||||
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||
import { PlatformIconButton } from '../common/PlatformIconButton';
|
||||
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
||||
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
||||
@@ -4070,9 +4071,13 @@ function ProfileSaveArchivesModal({
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-5 rounded-xl bg-zinc-50 px-4 py-5 text-center text-sm font-semibold text-zinc-500">
|
||||
<PlatformEmptyState
|
||||
surface="subpanel"
|
||||
size="inline"
|
||||
className="mt-5"
|
||||
>
|
||||
暂无存档
|
||||
</div>
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -4153,9 +4158,9 @@ function ProfilePlayedWorksModal({
|
||||
<div className="mt-5 space-y-5">
|
||||
{hasArchiveEntries ? (
|
||||
<section>
|
||||
<div className="mb-2 text-xs font-black text-zinc-500">
|
||||
<PlatformFieldLabel variant="section" className="mb-2 block">
|
||||
可继续
|
||||
</div>
|
||||
</PlatformFieldLabel>
|
||||
<div className="grid gap-3">
|
||||
{saveEntries.map((entry) => (
|
||||
<SaveArchiveCard
|
||||
@@ -4171,16 +4176,21 @@ function ProfilePlayedWorksModal({
|
||||
|
||||
{hasPlayedWorks ? (
|
||||
<section>
|
||||
<div className="mb-2 text-xs font-black text-zinc-500">
|
||||
<PlatformFieldLabel variant="section" className="mb-2 block">
|
||||
玩过
|
||||
</div>
|
||||
</PlatformFieldLabel>
|
||||
<div className="space-y-3">
|
||||
{playedWorks.map((work) => (
|
||||
<button
|
||||
<PlatformSubpanel
|
||||
as="button"
|
||||
type="button"
|
||||
key={`${work.worldKey}:${work.lastPlayedAt}`}
|
||||
onClick={() => onOpenWork?.(work)}
|
||||
className="w-full rounded-2xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-left transition hover:border-[#ff4056] hover:bg-white"
|
||||
surface="flat"
|
||||
radius="sm"
|
||||
padding="md"
|
||||
interactive
|
||||
className="w-full hover:border-[#ff4056]"
|
||||
>
|
||||
<div className="flex min-w-0 items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
@@ -4213,16 +4223,21 @@ function ProfilePlayedWorksModal({
|
||||
{formatCompactPlayTime(work.lastObservedPlayTimeMs)}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-5 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-4 text-sm text-zinc-600">
|
||||
<PlatformEmptyState
|
||||
surface="subpanel"
|
||||
size="inline"
|
||||
tone="base"
|
||||
className="mt-5 text-left"
|
||||
>
|
||||
暂无玩过
|
||||
</div>
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user