继续收口首页分类异步状态
扩展 PlatformAsyncStatePanel 接入首页分类分支的外层与筛选空态 合并桌面分类 section 并补充首页分类状态回归测试 修正 FlowShell 发现频道切换测试的 tab 语义断言 更新 PlatformUiKit 收口计划与共享决策记录
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
- 2026-06-11 追加:暗色 / 像素 modal 的标准 footer 布局统一抽到 `src/components/common/PlatformDarkModalFooter.tsx`;该组件只负责 dark footer 的分隔线、padding 和常见动作区排布,不持有“取消 / 确认”业务语义。`NpcModals.tsx` 的交易 / 赠礼 / 招募 footer、`SelectionCustomizationModals.tsx` 的 `SelectionModal` footer、`RpgAdventurePanelOverlays.tsx` 的 goal panel footer,以及 `InventoryItemViews.tsx` 的详情 footer wrapper 已接入;sticky 工作台 footer、正文内单 CTA 收尾和 runtime HUD 工具条暂不并入这一抽象。
|
||||
- 2026-06-11 追加:桌面首页里的轻量可点击扁平行开始统一收口到 `src/components/common/PlatformNavigableListItem.tsx`;目前已覆盖 `RpgEntryHomeView.tsx` 的搜索结果行、桌面“最近作品”、桌面“最近浏览”以及桌面“今日游戏”趋势行。组件只承接 `button + left content + right affordance` 结构、默认 `type="button"` 与 `leading / trailing` 插槽,暂不扩成覆盖教培 promo card、分类卡片、世界卡或 runtime 列表项的万能 row primitive。
|
||||
- 2026-06-11 追加:`PlatformNavigableListItem` 继续扩展到 profile 设置行;`src/components/platform-entry/PlatformProfilePrimitives.tsx` 的 `ProfileSettingsRow` 已改成委托共享 `button + leading + trailing` 骨架,继续保留本地 `platform-profile-settings-row` class 承接分隔线、icon 胶囊和字号微调。后续 profile / 账户中心里的同类轻量导航行优先直接复用共享行骨架,不再回退成原生 `<button>` 手写布局。
|
||||
- 2026-06-11 追加:`PlatformAsyncStatePanel` 继续补齐 RPG 首页分类分支;移动端“发现 -> 分类”、桌面发现页“分类”和桌面首页“作品分类”模块现在都统一委托共享状态壳切换外层 `loading / empty / content`,分类控制条与排序按钮继续留在内容 slot 中。筛选后无结果的“当前筛选下没有作品。”也统一改成内层 `PlatformAsyncStatePanel` 切换,不再在三处 JSX 中各自维护嵌套 ternary。
|
||||
- 2026-06-09 追加:通用输入 Composer 的上传参考图、发送和移除参考图已迁移到 `PlatformIconButton`;图标上传仍使用 `asChild="label"` 保留 label + file input 语义,公共组件会自动写入隐藏文本,确保内嵌 file input 继承可访问名称。
|
||||
- 2026-06-10 追加:creation-agent composer 的上传文档 / 上传参考图入口使用 `PlatformIconButton` 默认 `platformIcon`;工作台只保留动态 label、title、busy 状态和 picker 回调,发送按钮继续保留主题色动作布局。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
||||
- 2026-06-10 追加:作品详情顶部返回 / 分享和封面轮播上一张 / 下一张入口使用 `PlatformIconButton variant="platformIcon"`;详情页保留原 `platform-work-detail__*` 局部 class 控制位置和尺寸,点赞、复制三态等专用动作暂不迁移。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
||||
|
||||
@@ -262,6 +262,7 @@
|
||||
19.3.38. `PlatformAsyncStatePanel` 继续从 profile / 作品架扩展到首页公开分区:`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区都统一改成 `loadingState / emptyState / children` 三个 slot 切换。页面继续把 `platformError` 保留在状态壳外层,让错误提示可以和内容并存;`recommend runtime`、分类筛选和其它含二级筛选 / 运行态语义的分支暂不并入这次收口。后续首页、作品架或白底列表若只是纯 `loading / empty / content` 互斥状态,优先直接复用 `PlatformAsyncStatePanel`,不要再把空态与读取态分支手写回业务 JSX。验证命令:`npx vitest run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.39. 桌面首页里的轻量可点击扁平行统一收口到 `src/components/common/PlatformNavigableListItem.tsx`;该 Module 只承接 `button + 左侧主内容 + 右侧 affordance` 的结构、默认 `type="button"` 和 `leading / trailing` 插槽,不承接卡片封面、复杂摘要或 runtime 专属交互。`RpgEntryHomeView.tsx` 的搜索结果行、桌面“最近作品”、桌面“最近浏览”以及桌面“今日游戏”趋势行已接入。教培 promo card、分类卡片、世界卡和 runtime 列表项继续保留各自语义,等出现更多同构 desktop flat row 再逐步扩覆盖面。验证命令:`npx vitest run src/components/common/PlatformNavigableListItem.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.40. `PlatformNavigableListItem` 继续从桌面首页扩展到 profile 设置行:`src/components/platform-entry/PlatformProfilePrimitives.tsx` 里的 `ProfileSettingsRow` 现已统一委托共享 `button + leading + trailing` 骨架,保留本地 `platform-profile-settings-row` class 承接行间分隔、icon 胶囊和字号微调。后续 profile / 账户中心里同类“左图标标题 + 右箭头”的轻量导航行,优先直接复用 `PlatformNavigableListItem`,不要再回退成原生 `<button>` 手写布局。验证命令:`npx vitest run src/components/platform-entry/PlatformProfilePrimitives.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3.41. `PlatformAsyncStatePanel` 继续补齐首页分类分支:`RpgEntryHomeView.tsx` 的移动端“发现 -> 分类”、桌面发现页“分类”以及桌面首页“作品分类”模块都改成共享状态壳承接外层 `loading / empty / content` 切换,分类控制条与排序按钮继续保留在内容 slot 中;筛选后无结果的“当前筛选下没有作品。”也统一改由内层 `PlatformAsyncStatePanel` 切换,不再在三处 JSX 中各自手写空态分支。后续同类“外层数据可用性 + 内层筛选空态”面板优先沿用这套双层状态壳,不要回退成嵌套 ternary。验证命令:`npx vitest run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
19.3. creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`;首页继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏、抽屉和品牌化胶囊视觉,不把视觉回收和语义收口绑成一次大改。`Beta` 徽标和历史记录纯文本行暂保留本地实现,等出现更多同构轻量列表行后再评估是否抽新的共享 row primitive。
|
||||
19.4. 大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton variant="darkMini"`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布提交和文案状态语义,不再手写 hero 顶栏按钮壳。
|
||||
19.4.1. 大鱼吃小鱼结果页的发布失败弹层迁移到 `src/components/common/PlatformStatusDialog.tsx`;`PlatformStatusDialog` 补充自定义图标、可访问标签和动作按钮样式透传后,`BigFishResultView` 不再保留 `BigFishResultErrorModal` 内联的 `UnifiedConfirmDialog + PlatformIconBadge` 组合。结果页只保留失败文案和关闭回调,发布失败的状态图标、遮罩、白底面板和“知道了”主动作统一由共享状态弹层承接。验证命令:`npm run test -- src/components/common/PlatformStatusDialog.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`、`npm run typecheck`。
|
||||
|
||||
@@ -7561,7 +7561,7 @@ test('published puzzle works appear on home and mobile game category channel', a
|
||||
});
|
||||
|
||||
await clickFirstButtonByName(user, '发现');
|
||||
await user.click(screen.getByRole('button', { name: '分类' }));
|
||||
await user.click(screen.getByRole('tab', { name: '分类' }));
|
||||
|
||||
const discoverPanel = getPlatformTabPanel('category');
|
||||
expect(within(discoverPanel).getAllByText('星桥机关').length).toBeGreaterThan(
|
||||
@@ -8533,7 +8533,7 @@ test('published big fish works stay hidden from platform home and game category
|
||||
expect(screen.queryByText('机械深海 大鱼吃小鱼')).toBeNull();
|
||||
|
||||
await clickFirstButtonByName(user, '发现');
|
||||
await user.click(screen.getByRole('button', { name: '分类' }));
|
||||
await user.click(screen.getByRole('tab', { name: '分类' }));
|
||||
|
||||
const discoverPanel = getPlatformTabPanel('category');
|
||||
expect(within(discoverPanel).queryByText('机械深海 大鱼吃小鱼')).toBeNull();
|
||||
@@ -8573,7 +8573,7 @@ test('published puzzle detail returns to the ranking platform tab', async () =>
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await clickFirstButtonByName(user, '发现');
|
||||
await user.click(await screen.findByRole('button', { name: '排行' }));
|
||||
await user.click(await screen.findByRole('tab', { name: '排行' }));
|
||||
await waitFor(() => {
|
||||
expect(document.getElementById('platform-tab-panel-category')).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -911,6 +911,7 @@ function renderLoggedOutHomeView(
|
||||
| 'recommendRuntimeError'
|
||||
| 'onSelectNextRecommendEntry'
|
||||
| 'onSelectPreviousRecommendEntry'
|
||||
| 'isLoadingPlatform'
|
||||
>
|
||||
> = {},
|
||||
activeTab: RpgEntryHomeViewProps['activeTab'] = 'home',
|
||||
@@ -949,7 +950,7 @@ function renderLoggedOutHomeView(
|
||||
myEntries={[]}
|
||||
historyEntries={overrides.historyEntries ?? []}
|
||||
profileDashboard={null}
|
||||
isLoadingPlatform={false}
|
||||
isLoadingPlatform={overrides.isLoadingPlatform ?? false}
|
||||
isLoadingDashboard={false}
|
||||
isResumingSaveWorldKey={null}
|
||||
platformError={null}
|
||||
@@ -5488,6 +5489,52 @@ test('mobile game category filter dialog filters by play type', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('mobile game category keeps filter controls when current filter becomes empty', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderStatefulLoggedOutHomeView({
|
||||
latestEntries: [puzzlePublicEntry],
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '发现' }));
|
||||
await user.click(screen.getByRole('tab', { name: '分类' }));
|
||||
await user.click(screen.getByRole('button', { name: '奇幻' }));
|
||||
await user.click(screen.getByRole('button', { name: /筛选/u }));
|
||||
await user.click(screen.getByRole('button', { name: '抓鹅' }));
|
||||
|
||||
expect(screen.getByText('当前筛选下没有作品。')).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /筛选/u })).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: '奇幻' })).toBeTruthy();
|
||||
expect(document.querySelector('.platform-category-sort-button')).toBeTruthy();
|
||||
expect(screen.queryByRole('button', { name: /奇幻拼图,试玩/u })).toBeNull();
|
||||
expect(document.querySelector('.platform-category-game-item')).toBeNull();
|
||||
});
|
||||
|
||||
test('desktop discover category shows loading state before category data is ready', async () => {
|
||||
const user = userEvent.setup();
|
||||
mockDesktopLayout();
|
||||
|
||||
renderLoggedOutHomeView(
|
||||
vi.fn(),
|
||||
{
|
||||
isLoadingPlatform: true,
|
||||
},
|
||||
'category',
|
||||
true,
|
||||
);
|
||||
|
||||
const channelBar = document.querySelector('.platform-mobile-home-channelbar');
|
||||
if (!channelBar) {
|
||||
throw new Error('缺少发现频道栏');
|
||||
}
|
||||
|
||||
await user.click(within(channelBar as HTMLElement).getByRole('tab', { name: '分类' }));
|
||||
|
||||
expect(screen.getByText('正在读取作品分类...')).toBeTruthy();
|
||||
expect(screen.queryByRole('button', { name: /筛选/u })).toBeNull();
|
||||
expect(document.querySelector('.platform-category-game-item')).toBeNull();
|
||||
});
|
||||
|
||||
test('bottom category tab becomes ranking and switches ranking metrics', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
|
||||
@@ -3239,6 +3239,149 @@ export function RpgEntryHomeView({
|
||||
);
|
||||
const desktopFeaturedGrid = desktopRecommendEntries.slice(0, 4);
|
||||
const desktopCategoryGrid = activeCategoryEntries.slice(0, 6);
|
||||
const mobileCategoryPanelContent = activeCategoryGroup ? (
|
||||
<>
|
||||
<div className="platform-category-filter-row">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCategoryFilterPanelOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
className="platform-category-filter-button"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
<span>{categoryFilterApplied ? activeCategoryFilterLabel : '筛选'}</span>
|
||||
<span className="platform-category-filter-button__count">
|
||||
{activeCategoryFilterCount}
|
||||
</span>
|
||||
</button>
|
||||
<span className="platform-category-filter-divider" />
|
||||
<PlatformSegmentedTabs
|
||||
items={categoryGroupTabs}
|
||||
activeId={activeCategoryGroup.tag}
|
||||
onChange={handleCategoryGroupChange}
|
||||
layout="scroll"
|
||||
gap="md"
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
tone="neutral"
|
||||
className="platform-category-chip-scroll min-w-0 flex-1"
|
||||
itemClassName={(_, active) =>
|
||||
[
|
||||
'platform-category-chip shrink-0 !min-h-[2.35rem] !rounded-none !border-0 !bg-transparent !px-0 !shadow-none hover:!bg-transparent',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={cycleCategorySortMode}
|
||||
className="platform-category-sort-button"
|
||||
>
|
||||
<span>按{activeCategorySortLabel}排序</span>
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
<PlatformAsyncStatePanel
|
||||
isEmpty={activeCategoryEntries.length === 0}
|
||||
emptyState={<PlatformEmptyState>当前筛选下没有作品。</PlatformEmptyState>}
|
||||
>
|
||||
<div className="platform-category-game-list">
|
||||
{activeCategoryEntries.map((entry) => (
|
||||
<PlatformCategoryGameItem
|
||||
key={`${buildPublicGalleryCardKey(entry)}:mobile-category:${activeCategoryGroup.tag}:${categoryKindFilter}:${categorySortMode}`}
|
||||
entry={entry}
|
||||
categoryTag={activeCategoryGroup.tag}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</PlatformAsyncStatePanel>
|
||||
</>
|
||||
) : null;
|
||||
const renderDesktopCategorySection = (cardKeyPrefix: string) => {
|
||||
const desktopCategoryPanelContent = activeCategoryGroup ? (
|
||||
<>
|
||||
<div className="mb-4 flex min-w-0 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCategoryFilterPanelOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
className="platform-category-filter-button"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
<span>{categoryFilterApplied ? activeCategoryFilterLabel : '筛选'}</span>
|
||||
<span className="platform-category-filter-button__count">
|
||||
{activeCategoryFilterCount}
|
||||
</span>
|
||||
</button>
|
||||
<PlatformSegmentedTabs
|
||||
items={categoryGroupTabs}
|
||||
activeId={activeCategoryGroup.tag}
|
||||
onChange={handleCategoryGroupChange}
|
||||
layout="scroll"
|
||||
gap="md"
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
tone="neutral"
|
||||
className="min-w-0 flex-1 pb-1"
|
||||
itemClassName={(_, active) =>
|
||||
[
|
||||
'platform-category-chip shrink-0 !min-h-[2.35rem] !rounded-none !border-0 !bg-transparent !px-0 !shadow-none hover:!bg-transparent',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cycleCategorySortMode}
|
||||
className="platform-category-sort-button shrink-0"
|
||||
>
|
||||
<span>{activeCategorySortLabel}</span>
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<PlatformAsyncStatePanel
|
||||
isEmpty={desktopCategoryGrid.length === 0}
|
||||
emptyState={<PlatformEmptyState>当前筛选下没有作品。</PlatformEmptyState>}
|
||||
>
|
||||
<div className="grid gap-4 xl:grid-cols-3">
|
||||
{desktopCategoryGrid.map((entry) => (
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:${cardKeyPrefix}:${activeCategoryGroup.tag}:${categoryKindFilter}:${categorySortMode}`}
|
||||
entry={entry}
|
||||
onClick={() => openRecommendGalleryDetail(entry)}
|
||||
className="w-full min-w-0"
|
||||
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
|
||||
authorSummary={getPublicEntryAuthorSummary(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</PlatformAsyncStatePanel>
|
||||
</>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<section className="platform-desktop-panel px-5 py-5">
|
||||
<SectionHeader title="作品分类" detail="GAME CATEGORY" />
|
||||
<PlatformAsyncStatePanel
|
||||
isLoading={isLoadingPlatform}
|
||||
loadingState={<PlatformEmptyState>正在读取作品分类...</PlatformEmptyState>}
|
||||
isEmpty={!activeCategoryGroup || activeCategoryRawCount === 0}
|
||||
emptyState={<PlatformEmptyState>暂时还没有可分类的作品。</PlatformEmptyState>}
|
||||
>
|
||||
{desktopCategoryPanelContent}
|
||||
</PlatformAsyncStatePanel>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
const desktopLibraryPreview = myEntries.slice(0, 2);
|
||||
const resolvedRecommendCoverUrls = useResolvedRecommendCoverImages(
|
||||
recommendedFeedEntries,
|
||||
@@ -3753,81 +3896,18 @@ export function RpgEntryHomeView({
|
||||
mobileRankingPanel
|
||||
) : discoverChannel === 'category' ? (
|
||||
<section className="platform-category-list-panel">
|
||||
{isLoadingPlatform ? (
|
||||
<PlatformEmptyState>正在读取公开作品...</PlatformEmptyState>
|
||||
) : categoryGroups.length > 0 && activeCategoryGroup ? (
|
||||
<>
|
||||
<div className="platform-category-filter-row">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCategoryFilterPanelOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
className="platform-category-filter-button"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
<span>
|
||||
{categoryFilterApplied
|
||||
? activeCategoryFilterLabel
|
||||
: '筛选'}
|
||||
</span>
|
||||
<span className="platform-category-filter-button__count">
|
||||
{activeCategoryFilterCount}
|
||||
</span>
|
||||
</button>
|
||||
<span className="platform-category-filter-divider" />
|
||||
<PlatformSegmentedTabs
|
||||
items={categoryGroupTabs}
|
||||
activeId={activeCategoryGroup.tag}
|
||||
onChange={handleCategoryGroupChange}
|
||||
layout="scroll"
|
||||
gap="md"
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
tone="neutral"
|
||||
className="platform-category-chip-scroll min-w-0 flex-1"
|
||||
itemClassName={(_, active) =>
|
||||
[
|
||||
'platform-category-chip shrink-0 !min-h-[2.35rem] !rounded-none !border-0 !bg-transparent !px-0 !shadow-none hover:!bg-transparent',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={cycleCategorySortMode}
|
||||
className="platform-category-sort-button"
|
||||
>
|
||||
<span>按{activeCategorySortLabel}排序</span>
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
{activeCategoryEntries.length > 0 ? (
|
||||
<div className="platform-category-game-list">
|
||||
{activeCategoryEntries.map((entry) => (
|
||||
<PlatformCategoryGameItem
|
||||
key={`${buildPublicGalleryCardKey(entry)}:mobile-category:${activeCategoryGroup.tag}:${categoryKindFilter}:${categorySortMode}`}
|
||||
entry={entry}
|
||||
categoryTag={activeCategoryGroup.tag}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformEmptyState>
|
||||
当前筛选下没有作品。
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<PlatformEmptyState>
|
||||
公开广场暂时还没有可分类的作品。
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
<PlatformAsyncStatePanel
|
||||
isLoading={isLoadingPlatform}
|
||||
loadingState={<PlatformEmptyState>正在读取公开作品...</PlatformEmptyState>}
|
||||
isEmpty={!activeCategoryGroup || categoryGroups.length === 0}
|
||||
emptyState={
|
||||
<PlatformEmptyState>
|
||||
公开广场暂时还没有可分类的作品。
|
||||
</PlatformEmptyState>
|
||||
}
|
||||
>
|
||||
{mobileCategoryPanelContent}
|
||||
</PlatformAsyncStatePanel>
|
||||
</section>
|
||||
) : discoverChannel === 'edutainment' ? (
|
||||
<section className="platform-mobile-home-feed">
|
||||
@@ -3983,77 +4063,7 @@ export function RpgEntryHomeView({
|
||||
{discoverChannel === 'ranking' ? (
|
||||
mobileRankingPanel
|
||||
) : discoverChannel === 'category' ? (
|
||||
<section className="platform-desktop-panel px-5 py-5">
|
||||
<SectionHeader title="作品分类" detail="GAME CATEGORY" />
|
||||
{isLoadingPlatform ? (
|
||||
<PlatformEmptyState>正在读取作品分类...</PlatformEmptyState>
|
||||
) : activeCategoryGroup && activeCategoryRawCount > 0 ? (
|
||||
<>
|
||||
<div className="mb-4 flex min-w-0 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCategoryFilterPanelOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
className="platform-category-filter-button"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
<span>
|
||||
{categoryFilterApplied ? activeCategoryFilterLabel : '筛选'}
|
||||
</span>
|
||||
<span className="platform-category-filter-button__count">
|
||||
{activeCategoryFilterCount}
|
||||
</span>
|
||||
</button>
|
||||
<PlatformSegmentedTabs
|
||||
items={categoryGroupTabs}
|
||||
activeId={activeCategoryGroup.tag}
|
||||
onChange={handleCategoryGroupChange}
|
||||
layout="scroll"
|
||||
gap="md"
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
tone="neutral"
|
||||
className="min-w-0 flex-1 pb-1"
|
||||
itemClassName={(_, active) =>
|
||||
[
|
||||
'platform-category-chip shrink-0 !min-h-[2.35rem] !rounded-none !border-0 !bg-transparent !px-0 !shadow-none hover:!bg-transparent',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cycleCategorySortMode}
|
||||
className="platform-category-sort-button shrink-0"
|
||||
>
|
||||
<span>{activeCategorySortLabel}</span>
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{desktopCategoryGrid.length > 0 ? (
|
||||
<div className="grid gap-4 xl:grid-cols-3">
|
||||
{desktopCategoryGrid.map((entry) => (
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:desktop-discover-category:${activeCategoryGroup.tag}:${categoryKindFilter}:${categorySortMode}`}
|
||||
entry={entry}
|
||||
onClick={() => openRecommendGalleryDetail(entry)}
|
||||
className="w-full min-w-0"
|
||||
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
|
||||
authorSummary={getPublicEntryAuthorSummary(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformEmptyState>当前筛选下没有作品。</PlatformEmptyState>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<PlatformEmptyState>暂时还没有可分类的作品。</PlatformEmptyState>
|
||||
)}
|
||||
</section>
|
||||
renderDesktopCategorySection('desktop-discover-category')
|
||||
) : discoverChannel === 'edutainment' ? (
|
||||
<section className="platform-desktop-panel px-5 py-5">
|
||||
<SectionHeader title={EDUTAINMENT_WORK_TAG} detail="EDUTAINMENT" />
|
||||
@@ -4816,79 +4826,7 @@ export function RpgEntryHomeView({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<section className="platform-desktop-panel px-5 py-5">
|
||||
<SectionHeader title="作品分类" detail="GAME CATEGORY" />
|
||||
{isLoadingPlatform ? (
|
||||
<PlatformEmptyState>正在读取作品分类...</PlatformEmptyState>
|
||||
) : activeCategoryGroup && activeCategoryRawCount > 0 ? (
|
||||
<>
|
||||
<div className="mb-4 flex min-w-0 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCategoryFilterPanelOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
className="platform-category-filter-button"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
<span>
|
||||
{categoryFilterApplied
|
||||
? activeCategoryFilterLabel
|
||||
: '筛选'}
|
||||
</span>
|
||||
<span className="platform-category-filter-button__count">
|
||||
{activeCategoryFilterCount}
|
||||
</span>
|
||||
</button>
|
||||
<PlatformSegmentedTabs
|
||||
items={categoryGroupTabs}
|
||||
activeId={activeCategoryGroup.tag}
|
||||
onChange={handleCategoryGroupChange}
|
||||
layout="scroll"
|
||||
gap="md"
|
||||
frame="bare"
|
||||
surface="transparent"
|
||||
size="sm"
|
||||
tone="neutral"
|
||||
className="min-w-0 flex-1 pb-1"
|
||||
itemClassName={(_, active) =>
|
||||
[
|
||||
'platform-category-chip shrink-0 !min-h-[2.35rem] !rounded-none !border-0 !bg-transparent !px-0 !shadow-none hover:!bg-transparent',
|
||||
active ? 'platform-category-chip--active' : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cycleCategorySortMode}
|
||||
className="platform-category-sort-button shrink-0"
|
||||
>
|
||||
<span>{activeCategorySortLabel}</span>
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{desktopCategoryGrid.length > 0 ? (
|
||||
<div className="grid gap-4 xl:grid-cols-3">
|
||||
{desktopCategoryGrid.map((entry) => (
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:desktop-category:${activeCategoryGroup.tag}:${categoryKindFilter}:${categorySortMode}`}
|
||||
entry={entry}
|
||||
onClick={() => openRecommendGalleryDetail(entry)}
|
||||
className="w-full min-w-0"
|
||||
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
|
||||
authorSummary={getPublicEntryAuthorSummary(entry)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformEmptyState>当前筛选下没有作品。</PlatformEmptyState>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<PlatformEmptyState>暂时还没有可分类的作品。</PlatformEmptyState>
|
||||
)}
|
||||
</section>
|
||||
{renderDesktopCategorySection('desktop-category')}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user