refactor: 收口公开作品展示格式
This commit is contained in:
@@ -1249,6 +1249,14 @@
|
|||||||
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|edutainment"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "logged out home recommendation next starts the next puzzle work"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|edutainment"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "logged out home recommendation next starts the next puzzle work"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
- 关联文档:`docs/technical/【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md`。
|
- 关联文档:`docs/technical/【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md`。
|
||||||
|
|
||||||
|
## 2026-06-03 Public Work Presentation 收口
|
||||||
|
|
||||||
|
- 背景:作品卡、推荐 runtime meta、排行项、分类项、搜索结果和桌面 hero 共用玩法类型 label 与紧凑计数格式,但规则仍在 `RpgEntryHomeView.tsx` 页面 Implementation 内。
|
||||||
|
- 决策:在 `src/components/rpg-entry/rpgEntryWorldPresentation.ts` 追加单作品展示 Interface:`describePlatformPublicWorkKind` 与 `formatPlatformCompactCount`;页面删除本地实现。集合筛选、排序和指标选择继续留在 `rpgEntryPublicGalleryViewModel.ts`。
|
||||||
|
- 影响范围:公开作品卡片 aria label、推荐点赞 / 改造文案、排行数值、分类主指标、搜索结果和桌面 hero 玩法 label。
|
||||||
|
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|ranking|category"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
|
- 关联文档:`docs/technical/【前端架构】PublicWorkPresentation收口计划-2026-06-03.md`。
|
||||||
|
|
||||||
## 2026-06-03 Profile Funds ViewModel 收口
|
## 2026-06-03 Profile Funds ViewModel 收口
|
||||||
|
|
||||||
- 背景:个人资金展示规则散在 `RpgEntryHomeView.tsx`,且账单来源 label 表漏掉后端契约已有的 `puzzle_author_incentive_claim`,会把原始枚举值直接外显。
|
- 背景:个人资金展示规则散在 `RpgEntryHomeView.tsx`,且账单来源 label 表漏掉后端契约已有的 `puzzle_author_incentive_claim`,会把原始枚举值直接外显。
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
|||||||
|
|
||||||
公开作品分类、搜索、跨来源去重、今日筛选、排行排序和时间戳解析收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】PublicGalleryViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91PublicGalleryViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
公开作品分类、搜索、跨来源去重、今日筛选、排行排序和时间戳解析收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】PublicGalleryViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91PublicGalleryViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|
||||||
|
公开作品的玩法类型 label 与游玩 / 改造 / 点赞等紧凑计数格式收口到 `src/components/rpg-entry/rpgEntryWorldPresentation.ts`,规则见 [【前端架构】PublicWorkPresentation收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91PublicWorkPresentation%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|
||||||
推荐 feed 的公开作品去重、普通内容过滤、active 窗口与上一条 / 下一条回环选择也收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91RecommendFeedViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
推荐 feed 的公开作品去重、普通内容过滤、active 窗口与上一条 / 下一条回环选择也收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91RecommendFeedViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|
||||||
每日任务卡片与任务中心弹窗的任务选择、进度、状态标签和按钮文案收口到 `src/components/rpg-entry/rpgEntryProfileTaskViewModel.ts`,规则见 [【前端架构】ProfileTaskViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91ProfileTaskViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
每日任务卡片与任务中心弹窗的任务选择、进度、状态标签和按钮文案收口到 `src/components/rpg-entry/rpgEntryProfileTaskViewModel.ts`,规则见 [【前端架构】ProfileTaskViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91ProfileTaskViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 【前端架构】Public Work Presentation 收口计划
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
`RpgEntryHomeView.tsx` 的作品卡、推荐 runtime meta、排行项、分类项、搜索结果和桌面 hero 共用公开作品玩法类型 label 与紧凑计数格式。原先 `describePublicGalleryCardKind` 与 `formatCompactCount` 放在页面 **Implementation** 内,导致新增玩法或调整数字展示时需要穿过多段 JSX。
|
||||||
|
|
||||||
|
## 决策
|
||||||
|
|
||||||
|
在 `src/components/rpg-entry/rpgEntryWorldPresentation.ts` 追加单作品展示 **Interface**:
|
||||||
|
|
||||||
|
- `describePlatformPublicWorkKind(entry)`:统一公开作品玩法类型 label,并继续复用 `formatPlatformWorkDisplayTag` 的 4 字截断口径。
|
||||||
|
- `formatPlatformCompactCount(value)`:统一游玩、改造、点赞、排行和分类指标的紧凑数字展示。
|
||||||
|
|
||||||
|
`RpgEntryHomeView.tsx` 删除本地类型 label 与紧凑计数 **Implementation**,仅消费 `rpgEntryWorldPresentation.ts`。集合筛选、排序和指标选择仍留在 `rpgEntryPublicGalleryViewModel.ts`,避免单作品展示 **Module** 与集合 **Module** 混杂。
|
||||||
|
|
||||||
|
## 约定
|
||||||
|
|
||||||
|
- 紧凑计数保留既有口径:`10000` 显示 `1.0万`,`100000000` 显示 `1.0亿`,一万以下不加千分位。
|
||||||
|
- 玩法类型 label 继续遵循 4 字展示限制,例如“大鱼吃小鱼”外显为“大鱼吃小”。
|
||||||
|
- 本次不迁移排行 metric label / value 配对;该规则属于集合排序 **Module** 的后续切片。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- `npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts`
|
||||||
|
- `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|ranking|category"`
|
||||||
|
- 针对变更文件执行 ESLint
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run check:encoding`
|
||||||
@@ -180,19 +180,17 @@ import {
|
|||||||
} from './rpgEntryPublicGalleryViewModel';
|
} from './rpgEntryPublicGalleryViewModel';
|
||||||
import {
|
import {
|
||||||
buildPlatformWorldDisplayTags,
|
buildPlatformWorldDisplayTags,
|
||||||
|
describePlatformPublicWorkKind,
|
||||||
describePlatformThemeLabel,
|
describePlatformThemeLabel,
|
||||||
|
formatPlatformCompactCount,
|
||||||
formatPlatformWorkDisplayName,
|
formatPlatformWorkDisplayName,
|
||||||
formatPlatformWorkDisplayTag,
|
formatPlatformWorkDisplayTag,
|
||||||
formatPlatformWorldTime,
|
formatPlatformWorldTime,
|
||||||
isBarkBattleGalleryEntry,
|
isBarkBattleGalleryEntry,
|
||||||
isBigFishGalleryEntry,
|
isBigFishGalleryEntry,
|
||||||
isEdutainmentGalleryEntry,
|
isEdutainmentGalleryEntry,
|
||||||
isJumpHopGalleryEntry,
|
|
||||||
isMatch3DGalleryEntry,
|
|
||||||
isPuzzleGalleryEntry,
|
isPuzzleGalleryEntry,
|
||||||
isSquareHoleGalleryEntry,
|
|
||||||
isVisualNovelGalleryEntry,
|
isVisualNovelGalleryEntry,
|
||||||
isWoodenFishGalleryEntry,
|
|
||||||
type PlatformPublicGalleryCard,
|
type PlatformPublicGalleryCard,
|
||||||
resolvePlatformPublicWorkCode,
|
resolvePlatformPublicWorkCode,
|
||||||
resolvePlatformWorkAuthorDisplayName,
|
resolvePlatformWorkAuthorDisplayName,
|
||||||
@@ -638,14 +636,14 @@ function WorldCard({
|
|||||||
const playCount = getPlatformWorldPlayCount(entry);
|
const playCount = getPlatformWorldPlayCount(entry);
|
||||||
const remixCount = getPlatformWorldRemixCount(entry);
|
const remixCount = getPlatformWorldRemixCount(entry);
|
||||||
const likeCount = getPlatformWorldLikeCount(entry);
|
const likeCount = getPlatformWorldLikeCount(entry);
|
||||||
const typeLabel = describePublicGalleryCardKind(entry);
|
const typeLabel = describePlatformPublicWorkKind(entry);
|
||||||
const authorName = resolvePlatformWorkAuthorDisplayName(
|
const authorName = resolvePlatformWorkAuthorDisplayName(
|
||||||
entry,
|
entry,
|
||||||
authorSummary,
|
authorSummary,
|
||||||
);
|
);
|
||||||
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
|
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
|
||||||
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
|
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
|
||||||
const cardLabel = `${entry.worldName},${typeLabel},${formatCompactCount(playCount)}游玩,${formatCompactCount(remixCount)}改造,${formatCompactCount(likeCount)}点赞`;
|
const cardLabel = `${entry.worldName},${typeLabel},${formatPlatformCompactCount(playCount)}游玩,${formatPlatformCompactCount(remixCount)}改造,${formatPlatformCompactCount(likeCount)}点赞`;
|
||||||
const coverStats = [
|
const coverStats = [
|
||||||
{
|
{
|
||||||
label: '游玩',
|
label: '游玩',
|
||||||
@@ -716,7 +714,7 @@ function WorldCard({
|
|||||||
className={`h-3.5 w-3.5 ${label === '点赞' ? 'fill-current' : ''}`}
|
className={`h-3.5 w-3.5 ${label === '点赞' ? 'fill-current' : ''}`}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>{formatCompactCount(value)}</span>
|
<span>{formatPlatformCompactCount(value)}</span>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -769,7 +767,7 @@ function WorldCard({
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<span className="platform-pill platform-pill--neutral px-2.5">
|
<span className="platform-pill platform-pill--neutral px-2.5">
|
||||||
{describePublicGalleryCardKind(entry)}
|
{describePlatformPublicWorkKind(entry)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -892,7 +890,7 @@ function RecommendRuntimePreviewCard({
|
|||||||
}) {
|
}) {
|
||||||
const coverImage = resolvePlatformWorldCoverImage(entry);
|
const coverImage = resolvePlatformWorldCoverImage(entry);
|
||||||
const displayName = formatPlatformWorkDisplayName(entry.worldName);
|
const displayName = formatPlatformWorkDisplayName(entry.worldName);
|
||||||
const typeLabel = describePublicGalleryCardKind(entry);
|
const typeLabel = describePlatformPublicWorkKind(entry);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -1071,16 +1069,16 @@ function RecommendRuntimeMeta({
|
|||||||
onLike?.();
|
onLike?.();
|
||||||
}}
|
}}
|
||||||
disabled={!isActive || !onLike}
|
disabled={!isActive || !onLike}
|
||||||
aria-label={`点赞 ${formatCompactCount(likeCount)}`}
|
aria-label={`点赞 ${formatPlatformCompactCount(likeCount)}`}
|
||||||
title="点赞"
|
title="点赞"
|
||||||
>
|
>
|
||||||
<ThumbsUp className="h-5 w-5" aria-hidden="true" />
|
<ThumbsUp className="h-5 w-5" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
<span
|
<span
|
||||||
className="platform-recommend-work-meta__like-count"
|
className="platform-recommend-work-meta__like-count"
|
||||||
aria-label={`${formatCompactCount(likeCount)} 个赞`}
|
aria-label={`${formatPlatformCompactCount(likeCount)} 个赞`}
|
||||||
>
|
>
|
||||||
{formatCompactCount(likeCount)}
|
{formatPlatformCompactCount(likeCount)}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1111,7 +1109,7 @@ function RecommendRuntimeMeta({
|
|||||||
onRemix?.();
|
onRemix?.();
|
||||||
}}
|
}}
|
||||||
disabled={!isActive || !onRemix}
|
disabled={!isActive || !onRemix}
|
||||||
aria-label={`改造 ${formatCompactCount(remixCount)}`}
|
aria-label={`改造 ${formatPlatformCompactCount(remixCount)}`}
|
||||||
title="改造"
|
title="改造"
|
||||||
>
|
>
|
||||||
<GitFork className="h-5 w-5" aria-hidden="true" />
|
<GitFork className="h-5 w-5" aria-hidden="true" />
|
||||||
@@ -1323,7 +1321,7 @@ function DesktopTrendingItem({
|
|||||||
<div className="flex items-center gap-2 text-[10px] tracking-[0.18em] text-[var(--platform-text-soft)]">
|
<div className="flex items-center gap-2 text-[10px] tracking-[0.18em] text-[var(--platform-text-soft)]">
|
||||||
<span>{`${rank}`.padStart(2, '0')}</span>
|
<span>{`${rank}`.padStart(2, '0')}</span>
|
||||||
<span className="truncate">
|
<span className="truncate">
|
||||||
{describePublicGalleryCardKind(entry)}
|
{describePlatformPublicWorkKind(entry)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 line-clamp-1 text-lg font-semibold text-[var(--platform-text-strong)]">
|
<div className="mt-2 line-clamp-1 text-lg font-semibold text-[var(--platform-text-strong)]">
|
||||||
@@ -1350,7 +1348,7 @@ function DesktopTrendingItem({
|
|||||||
? '拼图'
|
? '拼图'
|
||||||
: isEdutainmentGalleryEntry(entry)
|
: isEdutainmentGalleryEntry(entry)
|
||||||
? entry.templateName
|
? entry.templateName
|
||||||
: describePublicGalleryCardKind(entry)}
|
: describePlatformPublicWorkKind(entry)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1407,11 +1405,11 @@ function PlatformRankingItem({
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||||
<span className="text-[var(--platform-warm-text)]">
|
<span className="text-[var(--platform-warm-text)]">
|
||||||
{formatCompactCount(metricValue)}
|
{formatPlatformCompactCount(metricValue)}
|
||||||
</span>
|
</span>
|
||||||
<span>{metricLabel}</span>
|
<span>{metricLabel}</span>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<span>{describePublicGalleryCardKind(entry)}</span>
|
<span>{describePlatformPublicWorkKind(entry)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="platform-ranking-item__tags">
|
<div className="platform-ranking-item__tags">
|
||||||
{tags.map((tag, index) => (
|
{tags.map((tag, index) => (
|
||||||
@@ -1440,7 +1438,7 @@ function PlatformCategoryGameItem({
|
|||||||
const tags = buildPlatformWorldDisplayTags(entry, 2);
|
const tags = buildPlatformWorldDisplayTags(entry, 2);
|
||||||
const metric = getPlatformCategoryPrimaryMetric(entry);
|
const metric = getPlatformCategoryPrimaryMetric(entry);
|
||||||
const metaParts = [
|
const metaParts = [
|
||||||
describePublicGalleryCardKind(entry),
|
describePlatformPublicWorkKind(entry),
|
||||||
...tags.filter((tag) => tag !== categoryTag),
|
...tags.filter((tag) => tag !== categoryTag),
|
||||||
].slice(0, 3);
|
].slice(0, 3);
|
||||||
const actionLabel =
|
const actionLabel =
|
||||||
@@ -1481,7 +1479,7 @@ function PlatformCategoryGameItem({
|
|||||||
<div className="platform-category-game-item__meta">
|
<div className="platform-category-game-item__meta">
|
||||||
<span className="platform-category-game-item__metric">
|
<span className="platform-category-game-item__metric">
|
||||||
<Star className="h-3.5 w-3.5 fill-current" />
|
<Star className="h-3.5 w-3.5 fill-current" />
|
||||||
<span>{formatCompactCount(metric.value)}</span>
|
<span>{formatPlatformCompactCount(metric.value)}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>{metric.label}</span>
|
<span>{metric.label}</span>
|
||||||
{metaParts.length > 0 ? <span>{metaParts.join(' · ')}</span> : null}
|
{metaParts.length > 0 ? <span>{metaParts.join(' · ')}</span> : null}
|
||||||
@@ -1672,7 +1670,7 @@ function PlatformWorkSearchResults({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="platform-pill platform-pill--neutral shrink-0 px-3">
|
<span className="platform-pill platform-pill--neutral shrink-0 px-3">
|
||||||
{describePublicGalleryCardKind(entry)}
|
{describePlatformPublicWorkKind(entry)}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -1711,51 +1709,10 @@ async function getPublicWorkAuthorSummary(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function describePublicGalleryCardKind(entry: PlatformPublicGalleryCard) {
|
|
||||||
if (isBigFishGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('大鱼吃小鱼');
|
|
||||||
}
|
|
||||||
if (isPuzzleGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('拼图');
|
|
||||||
}
|
|
||||||
if (isMatch3DGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('抓大鹅');
|
|
||||||
}
|
|
||||||
if (isSquareHoleGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('方洞挑战');
|
|
||||||
}
|
|
||||||
if (isJumpHopGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('跳一跳');
|
|
||||||
}
|
|
||||||
if (isWoodenFishGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('敲木鱼');
|
|
||||||
}
|
|
||||||
if (isVisualNovelGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('视觉小说');
|
|
||||||
}
|
|
||||||
if (isBarkBattleGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag('汪汪声浪');
|
|
||||||
}
|
|
||||||
if (isEdutainmentGalleryEntry(entry)) {
|
|
||||||
return formatPlatformWorkDisplayTag(entry.templateName);
|
|
||||||
}
|
|
||||||
return formatPlatformWorkDisplayTag(describePlatformThemeLabel(entry.themeMode));
|
|
||||||
}
|
|
||||||
function getPublicAuthorAvatarLabel(authorDisplayName: string) {
|
function getPublicAuthorAvatarLabel(authorDisplayName: string) {
|
||||||
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
|
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCompactCount(value: number) {
|
|
||||||
const normalizedValue = Math.max(0, Math.round(value));
|
|
||||||
if (normalizedValue >= 100000000) {
|
|
||||||
return `${(normalizedValue / 100000000).toFixed(1)}亿`;
|
|
||||||
}
|
|
||||||
if (normalizedValue >= 10000) {
|
|
||||||
return `${(normalizedValue / 10000).toFixed(1)}万`;
|
|
||||||
}
|
|
||||||
return `${normalizedValue}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSnapshotTime(value: string | null | undefined) {
|
function formatSnapshotTime(value: string | null | undefined) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return '刚刚保存';
|
return '刚刚保存';
|
||||||
@@ -6041,7 +5998,7 @@ export function RpgEntryHomeView({
|
|||||||
</span>
|
</span>
|
||||||
<span className="platform-pill platform-pill--neutral px-3">
|
<span className="platform-pill platform-pill--neutral px-3">
|
||||||
{leadPublicEntry
|
{leadPublicEntry
|
||||||
? describePublicGalleryCardKind(leadPublicEntry)
|
? describePlatformPublicWorkKind(leadPublicEntry)
|
||||||
: '作品'}
|
: '作品'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { expect, test } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
buildPlatformWorldDisplayTags,
|
buildPlatformWorldDisplayTags,
|
||||||
buildPuzzleWorkCoverSlides,
|
buildPuzzleWorkCoverSlides,
|
||||||
|
describePlatformPublicWorkKind,
|
||||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
|
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
|
||||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
|
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
|
||||||
|
formatPlatformCompactCount,
|
||||||
formatPlatformWorkDisplayName,
|
formatPlatformWorkDisplayName,
|
||||||
formatPlatformWorkDisplayTags,
|
formatPlatformWorkDisplayTags,
|
||||||
formatPlatformWorldTime,
|
formatPlatformWorldTime,
|
||||||
@@ -16,10 +18,11 @@ import {
|
|||||||
mapBarkBattleWorkToPlatformGalleryCard,
|
mapBarkBattleWorkToPlatformGalleryCard,
|
||||||
mapVisualNovelWorkToPlatformGalleryCard,
|
mapVisualNovelWorkToPlatformGalleryCard,
|
||||||
mapWoodenFishWorkToPlatformGalleryCard,
|
mapWoodenFishWorkToPlatformGalleryCard,
|
||||||
|
type PlatformBigFishGalleryCard,
|
||||||
type PlatformEdutainmentGalleryCard,
|
type PlatformEdutainmentGalleryCard,
|
||||||
type PlatformPuzzleGalleryCard,
|
type PlatformPuzzleGalleryCard,
|
||||||
resolvePlatformWorkAuthorDisplayName,
|
|
||||||
resolvePlatformPublicWorkCode,
|
resolvePlatformPublicWorkCode,
|
||||||
|
resolvePlatformWorkAuthorDisplayName,
|
||||||
resolvePlatformWorldFallbackCoverImage,
|
resolvePlatformWorldFallbackCoverImage,
|
||||||
} from './rpgEntryWorldPresentation';
|
} from './rpgEntryWorldPresentation';
|
||||||
|
|
||||||
@@ -53,6 +56,48 @@ test('platform work display text limits names and tags by character count', () =
|
|||||||
).toEqual(['超长机关', '星桥']);
|
).toEqual(['超长机关', '星桥']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform public work presentation formats compact counts and kind labels', () => {
|
||||||
|
const puzzleCard: PlatformPuzzleGalleryCard = {
|
||||||
|
sourceType: 'puzzle',
|
||||||
|
workId: 'puzzle-work-kind',
|
||||||
|
profileId: 'puzzle-profile-kind',
|
||||||
|
publicWorkCode: 'PZ-KIND',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
authorDisplayName: '玩家',
|
||||||
|
worldName: '机关拼图',
|
||||||
|
subtitle: '拼图关卡',
|
||||||
|
summaryText: '公开作品',
|
||||||
|
coverImageSrc: null,
|
||||||
|
themeTags: ['拼图'],
|
||||||
|
visibility: 'published',
|
||||||
|
publishedAt: '2026-05-18T00:00:00.000Z',
|
||||||
|
updatedAt: '2026-05-18T00:00:00.000Z',
|
||||||
|
};
|
||||||
|
const bigFishCard: PlatformBigFishGalleryCard = {
|
||||||
|
sourceType: 'big-fish',
|
||||||
|
workId: 'big-fish-work-kind',
|
||||||
|
profileId: 'big-fish-profile-kind',
|
||||||
|
publicWorkCode: 'BF-KIND',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
authorDisplayName: '玩家',
|
||||||
|
worldName: '大鱼海湾',
|
||||||
|
subtitle: '大鱼关卡',
|
||||||
|
summaryText: '公开作品',
|
||||||
|
coverImageSrc: null,
|
||||||
|
themeTags: ['大鱼'],
|
||||||
|
visibility: 'published',
|
||||||
|
publishedAt: '2026-05-18T00:00:00.000Z',
|
||||||
|
updatedAt: '2026-05-18T00:00:00.000Z',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(formatPlatformCompactCount(-1)).toBe('0');
|
||||||
|
expect(formatPlatformCompactCount(9999)).toBe('9999');
|
||||||
|
expect(formatPlatformCompactCount(10000)).toBe('1.0万');
|
||||||
|
expect(formatPlatformCompactCount(100000000)).toBe('1.0亿');
|
||||||
|
expect(describePlatformPublicWorkKind(puzzleCard)).toBe('拼图');
|
||||||
|
expect(describePlatformPublicWorkKind(bigFishCard)).toBe('大鱼吃小');
|
||||||
|
});
|
||||||
|
|
||||||
test('platform public cards use play type reference images as cover fallback', () => {
|
test('platform public cards use play type reference images as cover fallback', () => {
|
||||||
const puzzleCard: PlatformPuzzleGalleryCard = {
|
const puzzleCard: PlatformPuzzleGalleryCard = {
|
||||||
sourceType: 'puzzle',
|
sourceType: 'puzzle',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
|
||||||
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
|
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
|
||||||
|
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
||||||
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||||
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||||
import { BABY_OBJECT_MATCH_EDUTAINMENT_TAG } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
import { BABY_OBJECT_MATCH_EDUTAINMENT_TAG } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||||
@@ -863,6 +863,52 @@ export function formatPlatformWorkDisplayTags(
|
|||||||
].slice(0, limit);
|
].slice(0, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatPlatformCompactCount(value: number) {
|
||||||
|
const normalizedValue = Math.max(0, Math.round(value));
|
||||||
|
if (normalizedValue >= 100000000) {
|
||||||
|
return `${(normalizedValue / 100000000).toFixed(1)}亿`;
|
||||||
|
}
|
||||||
|
if (normalizedValue >= 10000) {
|
||||||
|
return `${(normalizedValue / 10000).toFixed(1)}万`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${normalizedValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function describePlatformPublicWorkKind(
|
||||||
|
entry: PlatformPublicGalleryCard,
|
||||||
|
) {
|
||||||
|
if (isBigFishGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('大鱼吃小鱼');
|
||||||
|
}
|
||||||
|
if (isPuzzleGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('拼图');
|
||||||
|
}
|
||||||
|
if (isMatch3DGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('抓大鹅');
|
||||||
|
}
|
||||||
|
if (isSquareHoleGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('方洞挑战');
|
||||||
|
}
|
||||||
|
if (isJumpHopGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('跳一跳');
|
||||||
|
}
|
||||||
|
if (isWoodenFishGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('敲木鱼');
|
||||||
|
}
|
||||||
|
if (isVisualNovelGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('视觉小说');
|
||||||
|
}
|
||||||
|
if (isBarkBattleGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag('汪汪声浪');
|
||||||
|
}
|
||||||
|
if (isEdutainmentGalleryEntry(entry)) {
|
||||||
|
return formatPlatformWorkDisplayTag(entry.templateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatPlatformWorkDisplayTag(describePlatformThemeLabel(entry.themeMode));
|
||||||
|
}
|
||||||
|
|
||||||
export function resolvePlatformWorkAuthorDisplayName(
|
export function resolvePlatformWorkAuthorDisplayName(
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
authorSummary?: PublicUserSummary | null,
|
authorSummary?: PublicUserSummary | null,
|
||||||
|
|||||||
Reference in New Issue
Block a user