Merge branch 'master' into codex/ddd

This commit is contained in:
2026-05-02 20:43:35 +08:00
101 changed files with 4552 additions and 1517 deletions

View File

@@ -1,5 +1,6 @@
import { ArrowRight } from 'lucide-react';
import { NEW_WORK_ENTRY_CONFIG } from '../../config/newWorkEntryConfig';
import { UnifiedModal } from '../common/UnifiedModal';
import { getVisiblePlatformCreationTypes } from './platformEntryCreationTypes';
@@ -86,8 +87,8 @@ export function PlatformEntryCreationTypeModal({
return (
<UnifiedModal
open={isOpen}
title="选择创作类型"
description="先选玩法类型,再进入对应创作工作台。"
title={NEW_WORK_ENTRY_CONFIG.typeModal.title}
description={NEW_WORK_ENTRY_CONFIG.typeModal.description}
onClose={onClose}
closeDisabled={isBusy}
size="lg"

File diff suppressed because it is too large Load Diff

View File

@@ -122,6 +122,24 @@ test('PlatformWorkDetailView calls like handler', () => {
expect(onLike).toHaveBeenCalledTimes(1);
});
test('PlatformWorkDetailView switches remix action label for owned work edit', () => {
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
actionMode="edit"
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByRole('button', { name: '作品编辑' })).toBeTruthy();
expect(screen.queryByRole('button', { name: '作品改造' })).toBeNull();
});
test('PlatformWorkDetailView cycles puzzle level cover slides', () => {
vi.useFakeTimers();
const { container } = render(

View File

@@ -8,6 +8,7 @@ import {
Gamepad2,
GitFork,
Heart,
PencilLine,
Play,
Share2,
} from 'lucide-react';
@@ -38,6 +39,7 @@ export interface PlatformWorkDetailViewProps {
onLike: () => void;
onStart: () => void;
onRemix: () => void;
actionMode?: 'remix' | 'edit';
}
function formatCompactCount(value: number) {
@@ -78,6 +80,7 @@ export function PlatformWorkDetailView({
onLike,
onStart,
onRemix,
actionMode = 'remix',
}: PlatformWorkDetailViewProps) {
const coverSlides = useMemo(
() => resolvePlatformWorldCoverSlides(entry),
@@ -111,6 +114,8 @@ export function PlatformWorkDetailView({
[entry],
);
const stats = resolvePlatformWorldStats(entry);
const workActionLabel = actionMode === 'edit' ? '作品编辑' : '作品改造';
const WorkActionIcon = actionMode === 'edit' ? PencilLine : GitFork;
const statItems = [
{
label: '游玩',
@@ -425,8 +430,8 @@ export function PlatformWorkDetailView({
onClick={onRemix}
disabled={isBusy}
>
<GitFork className="h-5 w-5" />
<WorkActionIcon className="h-5 w-5" />
{workActionLabel}
</button>
<button
type="button"

View File

@@ -0,0 +1,41 @@
import { expect, test } from 'vitest';
import { NEW_WORK_ENTRY_CONFIG } from '../../config/newWorkEntryConfig';
import {
getVisiblePlatformCreationTypes,
isPlatformCreationTypeVisible,
PLATFORM_CREATION_TYPES,
} from './platformEntryCreationTypes';
test('platform creation types are derived from new work entry config', () => {
const puzzleConfig = NEW_WORK_ENTRY_CONFIG.creationTypes.find(
(item) => item.id === 'puzzle',
);
expect(puzzleConfig).toBeTruthy();
expect(PLATFORM_CREATION_TYPES).toContainEqual(
expect.objectContaining({
id: 'puzzle',
title: puzzleConfig?.title,
subtitle: puzzleConfig?.subtitle,
badge: puzzleConfig?.badge,
locked: false,
hidden: false,
}),
);
});
test('new work entry config controls visibility and open order', () => {
const visibleIds = getVisiblePlatformCreationTypes().map((item) => item.id);
expect(isPlatformCreationTypeVisible('big-fish')).toBe(false);
expect(visibleIds).not.toContain('big-fish');
expect(visibleIds[0]).toBe('rpg');
expect(visibleIds).toEqual([
'rpg',
'puzzle',
'match3d',
'airp',
'visual-novel',
]);
});

View File

@@ -1,10 +1,9 @@
export type PlatformCreationTypeId =
| 'rpg'
| 'big-fish'
| 'match3d'
| 'puzzle'
| 'airp'
| 'visual-novel';
import {
NEW_WORK_ENTRY_CONFIG,
type NewWorkEntryCreationTypeId,
} from '../../config/newWorkEntryConfig';
export type PlatformCreationTypeId = NewWorkEntryCreationTypeId;
export type PlatformCreationTypeCard = {
id: PlatformCreationTypeId;
@@ -39,51 +38,15 @@ export function isPlatformCreationTypeVisible(id: PlatformCreationTypeId) {
}
/**
* 创作页与类型弹层共用同一份模板元数据,避免多入口文案和可用状态漂移。
* 创作页与类型弹层共用同一份新建作品入口配置,避免多入口文案和开放状态漂移。
* `hidden` 只控制平台入口是否展示,不影响既有玩法链路和路由能力。
*/
export const PLATFORM_CREATION_TYPES: PlatformCreationTypeCard[] = [
{
id: 'rpg',
title: '角色扮演',
subtitle: '敬请期待',
badge: '敬请期待',
locked: true,
},
{
id: 'big-fish',
title: '大鱼吃小鱼',
subtitle: '实时成长玩法',
badge: '可创建',
locked: false,
hidden: true,
},
{
id: 'puzzle',
title: '拼图',
subtitle: '创意礼物,生活分享',
badge: '可创建',
locked: false,
},
{
id: 'match3d',
title: '抓大鹅',
subtitle: '经典消除玩法',
badge: '可创建',
locked: false,
},
{
id: 'airp',
title: 'AIRP',
subtitle: '敬请期待',
badge: '敬请期待',
locked: true,
},
{
id: 'visual-novel',
title: '视觉小说',
subtitle: '敬请期待',
badge: '敬请期待',
locked: true,
},
];
export const PLATFORM_CREATION_TYPES: PlatformCreationTypeCard[] =
NEW_WORK_ENTRY_CONFIG.creationTypes.map((item) => ({
id: item.id,
title: item.title,
subtitle: item.subtitle,
badge: item.badge,
locked: !item.open,
hidden: !item.visible,
}));