实现 Makelore 一键提交审核

需求:让非专业用户在项目操作区一次提交,运营审核通过后直接发布。

实现:由 Electron Main 完成安全打包、自动版本、幂等重试和状态脱敏;补齐友好失败反馈、唯一提交入口及隔离 Electron E2E fixture。
This commit is contained in:
2026-08-08 14:44:08 +08:00
parent 7b23cce67a
commit 724290e864
18 changed files with 3056 additions and 31 deletions

View File

@@ -13,6 +13,7 @@ import { Sheet, SheetContent, SheetDescription, SheetTitle } from '@/components/
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { AgentCreationDialog, type AgentCreationInput } from '@/components/opencode/AgentCreationDialog';
import { ProjectPublishAction } from '@/components/works/ProjectPublishAction';
import { hostApiFetch } from '@/lib/host-api';
import { buildConfiguredModelOptions, type ConfiguredModelOption } from '@/lib/model-options';
import { getSkillDisplayInfo } from '@/lib/skill-display';
@@ -319,9 +320,12 @@ export function ProjectConfiguration() {
{!draft.initialized ? <div className="rounded-lg border border-foreground/15 bg-surface-tertiary px-5 py-3 text-sm font-semibold shadow-soft"></div> : null}
<section className="grid gap-3 md:grid-cols-3"><ResourceCard id="models" icon="🧠" title="大脑(大语言模型)" subtitle={draft.defaultModel ? `默认:${draft.defaultModel}` : '使用运行时默认模型'} onClick={() => setDrawerMode('models')} /><ResourceCard id="skills" icon="⚡" title="工具箱(技能)" subtitle={`已安装${skills.length}`} onClick={() => setDrawerMode('skills')} /><ResourceCard id="knowledge" icon="📓" title="笔记本(知识库)" subtitle={`已上传${knowledge.length}`} onClick={() => setDrawerMode('knowledge')} /></section>
<section className="config-motion-partners surface-card rounded-2xl border border-border/70 bg-background p-5 shadow-soft"><div className="mb-4 flex items-center justify-between gap-3"><div><h2 className="text-xl font-semibold"></h2><p className="mt-1 text-sm font-medium text-muted-foreground"></p></div><Button className="border border-brand/20 bg-brand-soft text-brand" onClick={() => { if (modelOptions.length === 0) { toast.error('请先在模型管理中配置至少一个可用模型。'); setDrawerMode('models'); return; } setCreatePartnerOpen(true); }}><Plus className="mr-2 h-4 w-4" /></Button></div><div data-testid="agent-cards-scroll-region" className="-m-2 flex gap-4 overflow-auto p-2">{draft.agents.filter((agent) => !agent.archivedAt).map((agent) => <AgentCard key={agent.id} agent={agent} onOpen={() => { setActiveAgentId(agent.id); setDrawerMode('agent'); }} />)}{draft.agents.every((agent) => agent.archivedAt) ? <div className="w-full rounded-xl border border-dashed border-foreground/15 p-6 text-center text-sm font-medium text-muted-foreground"></div> : null}</div>{draft.agents.some((agent) => agent.archivedAt) ? <div className="mt-4 border-t border-border/70 pt-4"><p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground"></p><div className="mt-2 flex flex-wrap gap-2">{draft.agents.filter((agent) => agent.archivedAt).map((agent) => <div key={agent.id} className="flex items-center gap-1"><Button type="button" variant="outline" className="border-border/70 bg-surface-subtle" onClick={() => updateAgent({ ...agent, archivedAt: null })}>{agent.name || '未命名联系人'} · </Button>{!agent.builtIn ? <Button type="button" variant="outline" size="icon" aria-label={'永久删除联系人 ' + (agent.name || '未命名联系人')} title="永久删除联系人" className="h-9 w-9 border-border/70 text-muted-foreground hover:bg-accent-soft hover:text-destructive" onClick={() => setDeleteAgentId(agent.id)}><Trash2 className="h-3.5 w-3.5" /></Button> : null}</div>)}</div></div> : null}</section>
<div className="glass-surface config-motion-actions sticky bottom-3 flex items-center justify-between gap-3 rounded-2xl border border-border/70 bg-background/85 p-2 shadow-float">
<div className="glass-surface config-motion-actions sticky bottom-3 flex flex-wrap items-start justify-between gap-3 rounded-2xl border border-border/70 bg-background/85 p-2 shadow-float">
<Button type="button" variant="outline" onClick={() => setDeleteDialogOpen(true)} className="h-12 border border-foreground/15 bg-accent-soft px-5 font-semibold text-foreground shadow-soft"><Trash2 className="mr-2 h-5 w-5" /></Button>
<Button onClick={() => void submit()} disabled={saving} className="project-save-button h-12 border border-brand/20 bg-brand px-6 font-semibold text-primary-foreground"><Check className="mr-2 h-5 w-5" />{saving ? '保存中…' : draft.initialized ? '保存项目配置' : '确认并完成初始化'}</Button>
<div className="ml-auto flex flex-wrap items-start justify-end gap-3">
<Button onClick={() => void submit()} disabled={saving} className="project-save-button h-12 border border-brand/20 bg-brand px-6 font-semibold text-primary-foreground"><Check className="mr-2 h-5 w-5" />{saving ? '保存中…' : draft.initialized ? '保存项目配置' : '确认并完成初始化'}</Button>
<ProjectPublishAction key={activeProject.id} project={activeProject} />
</div>
</div>
</div>
<AgentCreationDialog