feat: 脚本创作+发布+市场体系

- 数据模型新增: title(必填), description(可选), status(draft/published)
- 新增 API: POST /scripts/:id/publish, GET /api/market (搜索+分页+runtime过滤)
- 前端首页重构: 选语言 → CodeMirror 编辑器(8种语言语法高亮) → 标题/描述 → 草稿/发布
- 新增 /market 页面: 浏览已发布脚本, 搜索+过滤+分页
- 详情页新增: 发布按钮(草稿→市场), title/description 展示
- Shell 类运行时显示 source 命令(继承环境变量)
- backend GetSourceCommand 支持 bash/zsh/sh/fish 四种 shell 格式

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 14:04:15 +08:00
parent e3d380f9ab
commit e6e4357a28
16 changed files with 1051 additions and 183 deletions

View File

@@ -15,7 +15,9 @@ export default function ResultCard({ result, onReset }: Props) {
<div className="space-y-6">
<div className="text-center">
<div className="text-4xl mb-2"></div>
<h2 className="text-xl font-bold"></h2>
<h2 className="text-xl font-bold">
{result.status === 'published' ? '脚本已发布' : '草稿已创建'}
</h2>
</div>
<CommandCard command={result.command} />
@@ -34,6 +36,10 @@ export default function ResultCard({ result, onReset }: Props) {
)}
<div className="bg-gray-800/50 border border-gray-700 rounded-lg p-4 space-y-3">
<div className="flex justify-between text-sm">
<span className="text-gray-400"></span>
<span>{result.title}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-400"> ID</span>
<span className="font-mono text-blue-400">{result.id}</span>
@@ -43,19 +49,14 @@ export default function ResultCard({ result, onReset }: Props) {
<span>{result.runtime}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-400"></span>
<span>{new Date(result.expires_at).toLocaleString('zh-CN')}</span>
<span className="text-gray-400"></span>
<span className={result.status === 'published' ? 'text-green-400' : 'text-gray-400'}>
{result.status === 'published' ? '已发布' : '草稿'}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-400"></span>
<a
href={detailUrl}
target="_blank"
rel="noreferrer"
className="text-blue-400 hover:underline font-mono text-xs"
>
{detailUrl}
</a>
<span className="text-gray-400"></span>
<span>{new Date(result.expires_at).toLocaleString('zh-CN')}</span>
</div>
<div className="pt-2 border-t border-gray-700">
<div className="text-xs text-gray-500 mb-1"></div>
@@ -80,7 +81,17 @@ export default function ResultCard({ result, onReset }: Props) {
>
</a>
{result.status === 'published' && (
<a
href={`${window.location.origin}/market`}
target="_blank"
rel="noreferrer"
className="px-4 py-2 bg-green-600/80 hover:bg-green-600 rounded-lg text-sm transition-colors"
>
</a>
)}
</div>
</div>
)
}
}