feat(web): 用户工作台 - 产品化壳层、内容库布局与积分入口

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ddCatandCursor committed 2026-09-16 22:59:45 +08:00
1 parent f52702b70f
commit 969e2831b2
46 files changed
+5355 -944

No files matched your search

@@ -54,22 +54,22 @@ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, op
if (event.target !== event.currentTarget) return;
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
if (!editing) onClick();
if (!editing && !opening) onClick();
}
};
return (
<article className={cn("canvas-folder-card", selected && "is-selected", editing && "is-editing", opening && "is-opening")} onPointerEnter={onPrefetch} onPointerDown={onPrefetch} onFocusCapture={onPrefetch}>
<div className="canvas-folder-open" role="button" tabIndex={0} aria-label={`打开画布 ${project.title}`} aria-busy={opening} onClick={() => !editing && !opening && onClick()} onKeyDown={handleOpenKeyDown}>
<div className="canvas-folder-preview" aria-hidden="true">
<article className={cn("product-collection-card canvas-collection-card", selected && "is-selected", editing && "is-editing", opening && "is-opening")} onPointerEnter={onPrefetch} onPointerDown={onPrefetch} onFocusCapture={onPrefetch}>
<div className="canvas-collection-open" role="button" tabIndex={0} aria-label={`打开画布 ${project.title}`} aria-busy={opening} onClick={() => !editing && !opening && onClick()} onKeyDown={handleOpenKeyDown}>
<div className="canvas-collection-preview" aria-hidden="true">
<ProjectPreview project={{ id: project.id, nodes: project.previewNodes }} preferLatestImage />
{opening ? <div className="canvas-folder-opening"><LoaderCircle className="size-5 animate-spin" /><span>正在打开</span></div> : null}
{opening ? <div className="canvas-collection-opening"><LoaderCircle className="size-5 animate-spin" /><span>正在打开</span></div> : null}
</div>
<div className="canvas-folder-body">
<div className="canvas-folder-heading-row">
<div className="canvas-collection-body">
<div className="canvas-collection-heading-row">
{editing ? (
<Input
className="canvas-folder-title-input"
className="canvas-collection-title-input"
value={editingTitle}
onChange={(event) => setEditingTitle(event.target.value)}
onClick={(event) => event.stopPropagation()}
@@ -81,22 +81,19 @@ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, op
autoFocus
/>
) : (
<span className="canvas-folder-title">{project.title}</span>
<span className="canvas-collection-title">{project.title}</span>
)}
</div>
<div className="canvas-folder-meta">
<span className="canvas-folder-meta-item">{projectName ? `所属项目:${projectName}` : "自由画布"}</span>
<span className="canvas-folder-meta-separator" aria-hidden="true">·</span>
<span className="canvas-folder-meta-item">{project.nodeCount} 节点</span>
</div>
<div className="canvas-folder-dates">
<span><small>创建时间</small><time dateTime={project.createdAt}>{formatCanvasDate(project.createdAt)}</time></span>
<span><small>最后更新</small><time dateTime={project.updatedAt}>{formatCanvasDate(project.updatedAt)}</time></span>
<div className="canvas-collection-meta">
<span className="canvas-collection-meta-item">{projectName ? `所属项目:${projectName}` : "自由画布"}</span>
<span className="canvas-collection-meta-separator" aria-hidden="true">·</span>
<span className="canvas-collection-meta-item">{project.nodeCount} 节点</span>
</div>
<time className="canvas-collection-updated" dateTime={project.updatedAt} title={`创建于 ${formatCanvasDate(project.createdAt)}`}>{formatCanvasDate(project.updatedAt)} 更新</time>
</div>
</div>
<span className={cn("canvas-folder-select", selected && "is-visible")} onClick={(event) => event.stopPropagation()}>
<span className={cn("canvas-collection-select", selected && "is-visible")} onClick={(event) => event.stopPropagation()}>
<input
type="checkbox"
checked={selected}
@@ -105,11 +102,11 @@ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, op
/>
</span>
<div className="canvas-folder-actions" onClick={(event) => event.stopPropagation()}>
<div className="canvas-collection-actions" onClick={(event) => event.stopPropagation()}>
{!editing ? (
<button
type="button"
className="canvas-folder-rename"
className="product-icon-button canvas-collection-rename"
aria-label={`重命名 ${project.title}`}
title="重命名"
onClick={(event) => {
@@ -133,7 +130,7 @@ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, op
],
}}
>
<button type="button" className="canvas-folder-more" aria-label={`${project.title} 画布操作`} title="更多操作" onClick={(event) => event.stopPropagation()}>
<button type="button" className="product-icon-button canvas-collection-more" aria-label={`${project.title} 画布操作`} title="更多操作" onClick={(event) => event.stopPropagation()}>
<MoreHorizontal />
</button>
</Dropdown>