import React, { useEffect, useState } from 'react'; import { App, Button, Card, Col, Divider, Drawer, Empty, Form, Input, Modal, Popconfirm, Row, Select, Skeleton, Space, Switch, Tag, Tooltip, Typography, } from 'antd'; import { CopyOutlined, DeleteOutlined, EditOutlined, PlusOutlined, SaveOutlined, StarFilled } from '@ant-design/icons'; import ReactMarkdown from 'react-markdown'; import { useTranslation } from 'react-i18next'; import { useDict } from '../../hooks/useDict'; import { deletePromptTemplate, getPromptDetail, getPromptPage, savePromptTemplate, updatePromptStatus, updatePromptTemplate, type PromptTemplateVO, } from '../../api/business/prompt'; import { getHotWordGroupOptions, type HotWordGroupVO } from '../../api/business/hotwordGroup'; import AppPagination from '../../components/shared/AppPagination'; const { Option } = Select; const { Text, Title } = Typography; const PromptTemplates: React.FC = () => { const { message } = App.useApp(); const { t } = useTranslation(); const [form] = Form.useForm(); const [searchForm] = Form.useForm(); const { items: categories, loading: dictLoading } = useDict('biz_prompt_category'); const { items: dictTags } = useDict('biz_prompt_tag'); const { items: promptLevels } = useDict('biz_prompt_level'); const templateLevel = Form.useWatch('isSystem', form); const [loading, setLoading] = useState(false); const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [current, setCurrent] = useState(1); const [pageSize, setPageSize] = useState(12); const [drawerVisible, setDrawerVisible] = useState(false); const [editingId, setEditingId] = useState(null); const [submitLoading, setSubmitLoading] = useState(false); const [previewContent, setPreviewContent] = useState(''); const [groupOptions, setGroupOptions] = useState([]); const userProfile = React.useMemo(() => { const profileStr = sessionStorage.getItem("userProfile"); return profileStr ? JSON.parse(profileStr) : {}; }, []); const activeTenantId = React.useMemo(() => Number(localStorage.getItem("activeTenantId") || 0), []); const isPlatformAdmin = userProfile.isPlatformAdmin === true; const isTenantAdmin = userProfile.isTenantAdmin === true; useEffect(() => { void fetchData(); }, [current, pageSize]); useEffect(() => { void loadGroupOptions(); }, [isPlatformAdmin, templateLevel, activeTenantId]); const loadGroupOptions = async () => { const targetTenantId = isPlatformAdmin && Number(templateLevel) === 1 ? 0 : undefined; const res = await getHotWordGroupOptions(targetTenantId ?? (isPlatformAdmin && activeTenantId === 0 ? 0 : undefined)); setGroupOptions(res.data?.data || []); }; const fetchData = async () => { const values = searchForm.getFieldsValue(); setLoading(true); try { const res = await getPromptPage({ current, size: pageSize, name: values.name, category: values.category, }); if (res.data?.data) { setData(res.data.data.records); setTotal(res.data.data.total); } } finally { setLoading(false); } }; const handleStatusChange = async (id: number, checked: boolean) => { await updatePromptStatus(id, checked ? 1 : 0); message.success(checked ? '模板已启用' : '模板已停用'); await fetchData(); }; const handleOpenDrawer = (record?: PromptTemplateVO, isClone = false) => { if (record) { if (isClone) { setEditingId(null); form.setFieldsValue({ ...record, templateName: `${record.templateName} (副本)`, isSystem: 0, id: undefined, tenantId: undefined, }); setPreviewContent(record.promptContent); } else { const isPlatformLevel = Number(record.tenantId) === 0 && Number(record.isSystem) === 1; const currentUserId = userProfile.userId ? Number(userProfile.userId) : -1; let canEdit = false; if (Number(record.isSystem) === 0) { canEdit = Number(record.creatorId) === currentUserId; } else if (isPlatformAdmin) { canEdit = isPlatformLevel; } else if (isTenantAdmin) { canEdit = Number(record.tenantId) === activeTenantId; } if (!canEdit) { message.warning('您无权修改此层级的模板'); return; } setEditingId(record.id); form.setFieldsValue(record); setPreviewContent(record.promptContent); } } else { setEditingId(null); form.resetFields(); form.setFieldsValue({ status: 1, isSystem: (isTenantAdmin || isPlatformAdmin) ? 1 : 0, }); setPreviewContent(''); } setDrawerVisible(true); }; const showDetail = (record: PromptTemplateVO) => { void (async () => { const detailRes = await getPromptDetail(record.id); const detail = detailRes.data?.data || record; Modal.info({ title: record.templateName, width: 800, icon: null, content: (
{detail.description ? (
{detail.description}
) : null}
{detail.hotWordGroupName ? 热词组:{detail.hotWordGroupName} : 未绑定热词组} {(detail.tags || []).map((tag) => { const dictItem = dictTags.find((item) => item.itemValue === tag); return {dictItem ? dictItem.itemLabel : tag}; })}
{detail.hotWords && detail.hotWords.length > 0 ? (
绑定热词
{detail.hotWords.map((word) => {word})}
) : detail.hotWordGroupId ? (
该热词组当前没有热词
) : null} {detail.promptContent}
), okText: '关闭', maskClosable: true, }); })(); }; const handleSubmit = async () => { try { const values = await form.validateFields(); setSubmitLoading(true); if (!editingId && isPlatformAdmin && values.isSystem === 1) { values.tenantId = 0; } if (editingId) { await updatePromptTemplate({ ...values, id: editingId }); message.success('更新成功'); } else { await savePromptTemplate(values); message.success('模板已创建'); } setDrawerVisible(false); await fetchData(); } finally { setSubmitLoading(false); } }; const groupedData = React.useMemo(() => { const groups: Record = {}; data.forEach((item) => { const cat = item.category || 'default'; if (!groups[cat]) groups[cat] = []; groups[cat].push(item); }); return groups; }, [data]); const renderCard = (item: PromptTemplateVO) => { const isSystem = item.isSystem === 1; const isPlatformLevel = Number(item.tenantId) === 0 && isSystem; const isTenantLevel = Number(item.tenantId) > 0 && isSystem; const isPersonalLevel = !isSystem; const currentUserId = userProfile.userId ? Number(userProfile.userId) : -1; let canEdit = false; if (isPersonalLevel) { canEdit = Number(item.creatorId) === currentUserId; } else if (isPlatformAdmin) { canEdit = Number(item.tenantId) === 0; } else if (isTenantAdmin) { canEdit = Number(item.tenantId) === activeTenantId; } const levelTag = isPlatformLevel ? ( 平台级 ) : isTenantLevel ? ( 租户级 ) : ( 个人级 ); return ( showDetail(item)} style={{ width: 320, borderRadius: 12, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-card)', boxShadow: 'var(--app-shadow)', backdropFilter: 'blur(16px)', position: 'relative', overflow: 'hidden' }} styles={{ body: { padding: '24px' } }} >
{levelTag}
e.stopPropagation()} style={{ flexShrink: 0, marginLeft: 8 }}> {canEdit && handleOpenDrawer(item)} />} void handleStatusChange(item.id, checked)} />
{item.templateName} {item.description ? ( {item.description} ) : null}
{item.hotWordGroupName ? 热词组:{item.hotWordGroupName} : null} {item.tags?.map((tag) => { const dictItem = dictTags.find((dt) => dt.itemValue === tag); return ( {dictItem ? dictItem.itemLabel : tag} ); })} {!item.hotWordGroupName && (!item.tags || item.tags.length === 0) ? 未配置标签 : null}
e.stopPropagation()}> handleOpenDrawer(item, true)} /> {canEdit && ( deletePromptTemplate(item.id).then(() => fetchData())} okText={t('common.confirm')} cancelText={t('common.cancel')} > )}
); }; return (
提示词模板
void fetchData()}>
{Object.keys(groupedData).length === 0 ? (
) : ( <>
{Object.keys(groupedData).map((catKey) => { const catLabel = categories.find((c) => c.itemValue === catKey)?.itemLabel || catKey; return (
{catLabel}
{groupedData[catKey].map(renderCard)}
); })}
{ setCurrent(page); setPageSize(size); }} /> )}
{editingId ? '编辑模板' : '创建新模板'}} width="80%" onClose={() => setDrawerVisible(false)} open={drawerVisible} forceRender extra={ } destroyOnHidden >
{(isPlatformAdmin || isTenantAdmin) && ( )}