+
+
创建脚本
+
编写脚本,生成可分享的运行命令
+
+
+ {/* Runtime + Expires */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* Title + Description */}
+
+
+ {/* Code Editor */}
+
+
+
+
+
+
+
+ {/* Publish toggle */}
+
+
+ {!publish && (
+ (仅生成链接,不上架市场)
+ )}
+
+
+ {/* Submit */}
+ {error && (
+
+ {error}
+
)}
+
+
+
+
)
-}
+}
\ No newline at end of file
diff --git a/frontend/src/pages/Market.tsx b/frontend/src/pages/Market.tsx
new file mode 100644
index 0000000..e43a0fc
--- /dev/null
+++ b/frontend/src/pages/Market.tsx
@@ -0,0 +1,118 @@
+import { useState, useEffect, useCallback } from 'react'
+import { Link } from 'react-router-dom'
+import { listMarket } from '../lib/api'
+import { MarketItem, RUNTIME_OPTIONS } from '../types'
+
+export default function Market() {
+ const [items, setItems] = useState
([])
+ const [total, setTotal] = useState(0)
+ const [page, setPage] = useState(1)
+ const [runtime, setRuntime] = useState('')
+ const [search, setSearch] = useState('')
+ const [loading, setLoading] = useState(true)
+
+ const fetchMarket = useCallback(async () => {
+ setLoading(true)
+ try {
+ const res = await listMarket({ page, per_page: 20, runtime, search })
+ setItems(res.items)
+ setTotal(res.total)
+ } catch {
+ setItems([])
+ } finally {
+ setLoading(false)
+ }
+ }, [page, runtime, search])
+
+ useEffect(() => { fetchMarket() }, [fetchMarket])
+
+ const totalPages = Math.ceil(total / 20)
+
+ return (
+
+
+
脚本市场
+
浏览已发布的脚本,一键获取运行命令
+
+
+ {/* Search + Filter */}
+
+ { setSearch(e.target.value); setPage(1) }}
+ placeholder="搜索脚本..."
+ className="flex-1 px-4 py-2.5 bg-gray-800 border border-gray-700 rounded-lg text-sm focus:outline-none focus:border-blue-500"
+ />
+
+
+
+ {/* Results */}
+ {loading ? (
+
加载中...
+ ) : items.length === 0 ? (
+
+ ) : (
+
+ {items.map((item) => (
+
+
+
+ {item.title}
+
+
+ {item.runtime}
+
+
+ {item.description && (
+
{item.description}
+ )}
+
+ {item.id}
+
+
+ ))}
+
+ )}
+
+ {/* Pagination */}
+ {totalPages > 1 && (
+
+
+
+ {page} / {totalPages} ({total} 个脚本)
+
+
+
+ )}
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/pages/ScriptDetail.tsx b/frontend/src/pages/ScriptDetail.tsx
index 60b543c..5d82c5e 100644
--- a/frontend/src/pages/ScriptDetail.tsx
+++ b/frontend/src/pages/ScriptDetail.tsx
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'
import { useParams, Link } from 'react-router-dom'
import ScriptViewer from '../components/ScriptViewer'
import CommandCard from '../components/CommandCard'
-import { getScript } from '../lib/api'
+import { getScript, publishScript } from '../lib/api'
import { ScriptDetail as ScriptDetailType, isShellRuntime, getSourceCommand } from '../types'
export default function ScriptDetail() {
@@ -10,6 +10,8 @@ export default function ScriptDetail() {
const [script, setScript] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
+ const [publishing, setPublishing] = useState(false)
+ const [adminToken, setAdminToken] = useState('')
useEffect(() => {
if (!id) return
@@ -20,11 +22,7 @@ export default function ScriptDetail() {
}, [id])
if (loading) {
- return (
-
- )
+ return
}
if (error || !script) {
@@ -33,32 +31,55 @@ export default function ScriptDetail() {
😕
脚本不存在或已过期
{error}
-
- 创建新脚本
-
+ 创建新脚本