feat: add ChatEmptyState component and integrate it into ChatMessageList for improved user experience

This commit is contained in:
duanshuwen
2026-04-21 20:57:03 +08:00
parent a259907d12
commit 41c1949a2e
8 changed files with 172 additions and 34 deletions

View File

@@ -0,0 +1,48 @@
import { useI18n } from '../../i18n';
export default function ChatEmptyState() {
const { t } = useI18n();
const suggestions = [
{
key: 'task',
title: t('conversation.emptyState.suggestions.task.title'),
},
{
key: 'continuous',
title: t('conversation.emptyState.suggestions.continuous.title'),
},
{
key: 'parallel',
title: t('conversation.emptyState.suggestions.parallel.title'),
},
] as const;
return (
<div className="flex min-h-full items-center justify-center px-4 py-6">
<div className="flex w-full max-w-4xl flex-col items-center justify-center text-center">
<div className="h-[clamp(280px,56vh,520px)] w-full px-6 py-10 dark:bg-[radial-gradient(circle_at_top,rgba(49,73,109,0.24)_0%,rgba(24,24,27,0)_60%)]">
<div className="mx-auto flex h-full max-w-3xl flex-col items-center justify-center">
<h2
className="text-[clamp(40px,7vw,72px)] font-medium leading-[1.1] tracking-[-0.05em] text-[#2F3542] dark:text-[#E5E7EB]"
style={{ fontFamily: 'Georgia, "Songti SC", "STSong", serif' }}
>
{t('conversation.emptyState.title')}
</h2>
<div className="mt-9 flex max-w-3xl flex-wrap items-center justify-center gap-3">
{suggestions.map((suggestion) => (
<div
key={suggestion.key}
className="rounded-full border border-[#D8DEE8] bg-white/78 px-6 py-3 text-[15px] font-semibold text-[#4B5563] shadow-[0_8px_20px_rgba(15,23,42,0.04)] dark:border-[#3a3a40] dark:bg-[#202024] dark:text-gray-300"
>
{suggestion.title}
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,15 +1,18 @@
import { memo, useEffect, useRef } from 'react';
import type { ChatMessageItem } from './types';
import { useI18n } from '../../i18n';
import ChatEmptyState from './ChatEmptyState';
type ChatMessageListProps = {
messages: ChatMessageItem[];
loading?: boolean;
showWelcomeState?: boolean;
};
function ChatMessageList({ messages, loading }: ChatMessageListProps) {
function ChatMessageList({ messages, loading, showWelcomeState }: ChatMessageListProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
const { t } = useI18n();
const shouldShowWelcomeState = !loading && (showWelcomeState || messages.length === 0);
useEffect(() => {
const container = containerRef.current;
@@ -25,11 +28,7 @@ function ChatMessageList({ messages, loading }: ChatMessageListProps) {
{t('conversation.messageList.loading')}
</div>
) : null}
{!loading && messages.length === 0 ? (
<div className="rounded-[18px] border border-dashed border-[#BEDBFF] bg-[#EFF6FF] px-4 py-6 text-sm leading-7 text-[#525866] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:text-gray-400">
{t('conversation.messageList.emptyHint')}
</div>
) : null}
{shouldShowWelcomeState ? <ChatEmptyState /> : null}
{messages.map((message) => (
<article
key={message.id}

View File

@@ -1,4 +1,5 @@
export { default as ChatComposer } from './ChatComposer';
export { default as ChatEmptyState } from './ChatEmptyState';
export { default as ChatHistoryPanel } from './ChatHistoryPanel';
export { default as ChatMessageList } from './ChatMessageList';
export { default as TaskBoard } from './TaskBoard';