feat(ux): 空状态组件,替换各页面空白提示

新增 EmptyState 组件,ProjectPage 暂无任务时显示带操作按钮的
友好提示,AssetPreview 暂无素材时显示引导文案。
This commit is contained in:
2026-05-24 21:23:02 +08:00
parent f063ec564e
commit aaf846a60d
3 changed files with 53 additions and 9 deletions
+2 -5
View File
@@ -1,4 +1,5 @@
import type { Asset } from '../api/types' import type { Asset } from '../api/types'
import EmptyState from './EmptyState'
interface AssetPreviewProps { interface AssetPreviewProps {
assets: Asset[] assets: Asset[]
@@ -6,11 +7,7 @@ interface AssetPreviewProps {
export default function AssetPreview({ assets }: AssetPreviewProps) { export default function AssetPreview({ assets }: AssetPreviewProps) {
if (assets.length === 0) { if (assets.length === 0) {
return ( return <EmptyState title="暂无素材" description="生成完成后,素材将在这里展示" />
<div style={{ color: 'var(--text-muted)', textAlign: 'center', padding: 40 }}>
暂无素材
</div>
)
} }
return ( return (
+43
View File
@@ -0,0 +1,43 @@
interface EmptyStateProps {
icon?: string
title: string
description?: string
action?: {
label: string
onClick: () => void
}
}
export default function EmptyState({ icon = '📭', title, description, action }: EmptyStateProps) {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '48px 24px',
textAlign: 'center',
}}
>
<span style={{ fontSize: 48, marginBottom: 16, opacity: 0.6 }}>{icon}</span>
<h3 style={{ fontSize: 16, color: 'var(--text-secondary)', marginBottom: 8 }}>
{title}
</h3>
{description && (
<p style={{ fontSize: 13, color: 'var(--text-muted)', marginBottom: action ? 20 : 0 }}>
{description}
</p>
)}
{action && (
<button
className="btn-primary"
onClick={action.onClick}
style={{ padding: '8px 24px', fontSize: 13 }}
>
{action.label}
</button>
)}
</div>
)
}
+8 -4
View File
@@ -1,10 +1,11 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom' import { Link, useNavigate, useParams } from 'react-router-dom'
import { useProjectStore } from '../stores/project' import { useProjectStore } from '../stores/project'
import { getTasks } from '../api/project' import { getTasks } from '../api/project'
import type { Task } from '../api/types' import type { Task } from '../api/types'
import StyleSelector from '../components/StyleSelector' import StyleSelector from '../components/StyleSelector'
import Skeleton from '../components/Skeleton' import Skeleton from '../components/Skeleton'
import EmptyState from '../components/EmptyState'
const STATUS_LABELS: Record<string, { label: string; color: string }> = { const STATUS_LABELS: Record<string, { label: string; color: string }> = {
pending: { label: '等待中', color: 'var(--text-muted)' }, pending: { label: '等待中', color: 'var(--text-muted)' },
@@ -15,6 +16,7 @@ const STATUS_LABELS: Record<string, { label: string; color: string }> = {
export default function ProjectPage() { export default function ProjectPage() {
const { projectId = 'proj-default' } = useParams() const { projectId = 'proj-default' } = useParams()
const navigate = useNavigate()
const { const {
name, name,
style, style,
@@ -95,9 +97,11 @@ export default function ProjectPage() {
</div> </div>
{tasks.length === 0 ? ( {tasks.length === 0 ? (
<p style={{ color: 'var(--text-muted)', textAlign: 'center', padding: 32 }}> <EmptyState
暂无任务 title="暂无任务"
</p> description="点击「新建生成」开始创建你的第一个游戏素材"
action={{ label: '新建生成', onClick: () => navigate(`/projects/${projectId}/generate`) }}
/>
) : ( ) : (
<table style={{ width: '100%', borderCollapse: 'collapse' }}> <table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead> <thead>