'use client' import { useState } from 'react' import useSWR from 'swr' import { api } from '@/lib/api-client' import type { PromptTemplate, PromptVersion } from '@/lib/types' import { EmptyState } from '@/components/ui/state' import { TableSkeleton } from '@/components/ui/skeleton' export default function PromptsPage() { const [page, setPage] = useState(1) const [selectedName, setSelectedName] = useState(null) const [versions, setVersions] = useState([]) const [showCreate, setShowCreate] = useState(false) const [showNewVersion, setShowNewVersion] = useState(false) const [filter, setFilter] = useState<{ source?: string; status?: string }>({}) const { data, error, isLoading, mutate } = useSWR( ['prompts.list', page, filter.source, filter.status], () => api.prompts.list({ page, page_size: 50, ...filter }), ) const templates = data?.items ?? [] const total = data?.total ?? 0 const fetchVersions = async (name: string) => { try { const res = await api.prompts.listVersions(name) setVersions(res) setSelectedName(name) } catch (err) { console.error('Failed to fetch versions:', err) } } const handleCreate = async (e: React.FormEvent) => { e.preventDefault() const fd = new FormData(e.currentTarget) try { await api.prompts.create({ name: fd.get('name') as string, category: fd.get('category') as string, description: (fd.get('description') as string) || undefined, source: 'custom', system_prompt: fd.get('system_prompt') as string, }) setShowCreate(false) mutate() } catch (err) { console.error('Failed to create prompt:', err) } } const handleNewVersion = async (e: React.FormEvent) => { e.preventDefault() if (!selectedName) return const fd = new FormData(e.currentTarget) try { await api.prompts.createVersion(selectedName, { system_prompt: fd.get('system_prompt') as string, changelog: (fd.get('changelog') as string) || undefined, }) setShowNewVersion(false) fetchVersions(selectedName) } catch (err) { console.error('Failed to create version:', err) } } const handleRollback = async (name: string, version: number) => { if (!confirm(`确认回退到版本 ${version}?`)) return try { await api.prompts.rollback(name, version) fetchVersions(name) mutate() } catch (err) { console.error('Failed to rollback:', err) } } const handleArchive = async (name: string) => { if (!confirm(`确认归档 ${name}?`)) return try { await api.prompts.archive(name) mutate() } catch (err) { console.error('Failed to archive:', err) } } const statusBadge = (status: string) => { const colors: Record = { active: 'bg-emerald-500/20 text-emerald-400', deprecated: 'bg-amber-500/20 text-amber-400', archived: 'bg-zinc-500/20 text-zinc-400', } return ( {status} ) } const sourceBadge = (source: string) => { const colors: Record = { builtin: 'bg-blue-500/20 text-blue-400', custom: 'bg-purple-500/20 text-purple-400', } return ( {source === 'builtin' ? '内置' : '自定义'} ) } return (
{/* Header */}

提示词管理

管理内置和自定义提示词模板,支持版本控制和 OTA 分发

{/* Filters */}
{(['all', 'builtin', 'custom'] as const).map(s => ( ))}
{/* Template List */}
{isLoading ? ( ) : error ? ( ) : templates.length === 0 ? ( ) : ( templates.map(t => ( )) )}
名称 分类 来源 版本 状态 更新时间 操作
加载失败
{t.category} {sourceBadge(t.source)} v{t.current_version} {statusBadge(t.status)} {new Date(t.updated_at).toLocaleString('zh-CN')} {t.source === 'custom' && ( )}
共 {total} 个模板
{/* Version History Panel */} {selectedName && (

{selectedName} — 版本历史

{versions.map(v => (
v{v.version}
{new Date(v.created_at).toLocaleString('zh-CN')} {v.changelog && ( — {v.changelog} )} {v.min_app_version && ( 最低版本: {v.min_app_version} )}
                  {v.system_prompt.substring(0, 300)}{v.system_prompt.length > 300 ? '...' : ''}
                
))} {versions.length === 0 && ( )}
)} {/* Create Modal */} {showCreate && (

新建提示词模板