import { useState, useEffect } from 'react'; import { Card, Tabs, Button, Switch, Modal, Input, Tag, message, Space, Typography, Row, Col, Alert, Upload, Spin, Empty, theme } from 'antd'; import { EditOutlined, ReloadOutlined, DownloadOutlined, UploadOutlined, CheckCircleOutlined, FileSearchOutlined, InfoCircleOutlined } from '@ant-design/icons'; import axios from 'axios'; import { promptTemplateCardStyles, promptTemplateCardHoverHandlers, promptTemplateGridConfig } from '../components/CardStyles'; const { TextArea } = Input; const { Title, Text, Paragraph } = Typography; interface PromptTemplate { id: string; user_id: string; template_key: string; template_name: string; template_content: string; description: string; category: string; parameters: string; is_active: boolean; is_system_default: boolean; created_at: string; updated_at: string; } interface CategoryGroup { category: string; count: number; templates: PromptTemplate[]; } export default function PromptTemplates() { const { token } = theme.useToken(); const [modal, contextHolder] = Modal.useModal(); const [categories, setCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState('0'); const [editingTemplate, setEditingTemplate] = useState(null); const [editorVisible, setEditorVisible] = useState(false); const [loading, setLoading] = useState(false); const isMobile = window.innerWidth <= 768; // 加载模板数据 const loadTemplates = async () => { try { setLoading(true); const response = await axios.get('/api/prompt-templates/categories'); setCategories(response.data); } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '加载失败'); } finally { setLoading(false); } }; useEffect(() => { loadTemplates(); }, []); // 获取当前分类的模板 const getCurrentTemplates = (): PromptTemplate[] => { const index = parseInt(selectedCategory); if (index === 0) { return categories.flatMap(cat => cat.templates); } return categories[index - 1]?.templates || []; }; // 编辑模板 const handleEdit = (template: PromptTemplate) => { setEditingTemplate({ ...template }); setEditorVisible(true); }; // 保存模板 const handleSave = async () => { if (!editingTemplate) return; try { setLoading(true); await axios.post('/api/prompt-templates', { template_key: editingTemplate.template_key, template_name: editingTemplate.template_name, template_content: editingTemplate.template_content, description: editingTemplate.description, category: editingTemplate.category, parameters: editingTemplate.parameters, is_active: editingTemplate.is_active }); message.success('保存成功'); setEditorVisible(false); loadTemplates(); } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '保存失败'); } finally { setLoading(false); } }; // 重置为系统默认 const handleReset = async (templateKey: string) => { modal.confirm({ title: '确认重置', content: '确定要重置为系统默认模板吗?这将覆盖您的自定义内容。', okText: '确定', cancelText: '取消', centered: true, onOk: async () => { try { setLoading(true); await axios.post(`/api/prompt-templates/${templateKey}/reset`); message.success('已重置为系统默认'); loadTemplates(); } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '重置失败'); } finally { setLoading(false); } } }); }; // 切换启用状态 const handleToggleActive = async (template: PromptTemplate, checked: boolean) => { try { await axios.put(`/api/prompt-templates/${template.template_key}`, { is_active: checked }); loadTemplates(); } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '操作失败'); } }; // 导出所有模板 const handleExport = async () => { try { const response = await axios.post('/api/prompt-templates/export'); const stats = response.data.statistics; const blob = new Blob([JSON.stringify(response.data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `prompt-templates-${new Date().toISOString().split('T')[0]}.json`; a.click(); URL.revokeObjectURL(url); if (stats) { message.success( `成功导出 ${stats.total} 个提示词配置(${stats.customized} 个自定义,${stats.system_default} 个系统默认)`, 5 ); } else { message.success('导出成功'); } } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '导出失败'); } }; // 导入模板 const handleImport = async (file: File) => { try { const text = await file.text(); const data = JSON.parse(text); const response = await axios.post('/api/prompt-templates/import', data); const result = response.data; const stats = result.statistics; // 构建详细的成功消息 let successMsg = `导入成功!\n`; if (stats) { successMsg += `• 保持系统默认:${stats.kept_system_default} 个\n`; successMsg += `• 创建/更新自定义:${stats.created_or_updated} 个`; if (stats.converted_to_custom > 0) { successMsg += `\n• 检测到修改(已转为自定义):${stats.converted_to_custom} 个`; } } // 如果有被转换的模板,显示详细信息 if (result.converted_templates && result.converted_templates.length > 0) { modal.info({ title: '导入完成', width: 600, centered: true, content: (

{successMsg}

{result.converted_templates.length > 0 && (

以下模板内容与系统默认不一致,已转为自定义:

    {result.converted_templates.map((t: { template_key: string; template_name: string }) => (
  • {t.template_name} ({t.template_key})
  • ))}
)}
), okText: '确定' }); } else { message.success(successMsg, 5); } loadTemplates(); } catch (error: unknown) { const err = error as { response?: { data?: { detail?: string } } }; message.error(err.response?.data?.detail || '导入失败'); } return false; // 阻止默认上传行为 }; const currentTemplates = getCurrentTemplates(); const pageBackground = `linear-gradient(180deg, ${token.colorBgLayout} 0%, ${token.colorFillSecondary} 100%)`; const headerBackground = `linear-gradient(135deg, ${token.colorPrimary} 0%, ${token.colorPrimaryHover} 100%)`; return ( <> {contextHolder}
{/* 顶部导航卡片 */} {/* 装饰性背景元素 */}
<FileSearchOutlined style={{ color: token.colorWhite, opacity: 0.9, marginRight: 8 }} /> 提示词模板管理 自定义AI生成提示词,打造个性化创作体验 {/* 使用提示 */} 使用说明 } description={
系统默认模板(灰色头部):始终启用,无需手动开关。点击"编辑"后将创建您的自定义副本。 已自定义模板(紫色头部):可通过开关控制启用/禁用,使用 {'{variable_name}'} 格式表示变量占位符。点击"重置"可恢复为系统默认。
} type="info" showIcon={false} style={{ marginTop: isMobile ? 16 : 24, borderRadius: 12, background: token.colorInfoBg, border: `1px solid ${token.colorInfoBorder}` }} /> {/* 主内容区 */}
{/* 分类标签 */} {categories.length > 0 && ( sum + cat.count, 0)})` }, ...categories.map((cat, index) => ({ key: (index + 1).toString(), label: `${cat.category} (${cat.count})` })) ]} /> )} {/* 模板列表 */} {currentTemplates.length === 0 ? ( ) : ( {currentTemplates.map(template => ( {/* 头部 */}
{template.template_name} {!template.is_system_default && ( handleToggleActive(template, checked)} size={isMobile ? 'small' : 'default'} style={{ marginLeft: 8 }} /> )}
{template.category} {template.is_system_default ? '系统默认' : '已自定义'}
{/* 内容 */}
{template.description || '暂无描述'} } color={template.is_system_default || template.is_active ? 'success' : 'default'} > {template.is_system_default ? '始终启用' : (template.is_active ? '已启用' : '已禁用')} 模板键: {template.template_key} {/* 操作按钮 */}
))}
)}
{/* 编辑对话框 */} setEditorVisible(false)} onOk={handleSave} width={isMobile ? '100%' : 900} centered={!isMobile} confirmLoading={loading} okText="保存" cancelText="取消" style={isMobile ? { top: 0, paddingBottom: 0, maxWidth: '100vw' } : undefined} styles={isMobile ? { body: { maxHeight: 'calc(100vh - 110px)', overflowY: 'auto', padding: '16px' } } : undefined} >
setEditingTemplate(prev => prev ? { ...prev, template_name: e.target.value } : null)} placeholder="输入模板名称" />