feat(ux): 空状态组件,替换各页面空白提示
新增 EmptyState 组件,ProjectPage 暂无任务时显示带操作按钮的 友好提示,AssetPreview 暂无素材时显示引导文案。
This commit is contained in:
@@ -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 (
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user