diff --git a/apps/admin-web/src/pages/KnowledgeBasesPage.tsx b/apps/admin-web/src/pages/KnowledgeBasesPage.tsx index b79de38..df34fc7 100644 --- a/apps/admin-web/src/pages/KnowledgeBasesPage.tsx +++ b/apps/admin-web/src/pages/KnowledgeBasesPage.tsx @@ -18,6 +18,7 @@ import type { ColumnsType } from 'antd/es/table'; import { UploadOutlined } from '@ant-design/icons'; import type { KnowledgeBaseDto, + KnowledgeDocumentDetailDto, KnowledgeDocumentDto, } from '@dukang/shared-types'; import { request } from '../lib/api'; @@ -49,6 +50,10 @@ export default function KnowledgeBasesPage() { const [docsLoading, setDocsLoading] = useState(false); const [docModalOpen, setDocModalOpen] = useState(false); const [docSaving, setDocSaving] = useState(false); + const [editingDoc, setEditingDoc] = useState(null); + const [editDocModalOpen, setEditDocModalOpen] = useState(false); + const [editDocSaving, setEditDocSaving] = useState(false); + const [editDocForm] = Form.useForm(); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( @@ -195,28 +200,52 @@ export default function KnowledgeBasesPage() { }, { title: '操作', - width: 80, + width: 140, render: (_, row) => drawerKb?.canEditFull ? ( - { - try { - await request(`/admin/knowledge-bases/${drawerKb.id}/documents/${row.id}`, { - method: 'DELETE', - }); - message.success('已删除'); - void loadDocs(drawerKb); - reload(); - } catch (e) { - message.error(e instanceof Error ? e.message : String(e)); - } - }} - > - - + { + try { + await request(`/admin/knowledge-bases/${drawerKb.id}/documents/${row.id}`, { + method: 'DELETE', + }); + message.success('已删除'); + void loadDocs(drawerKb); + reload(); + } catch (e) { + message.error(e instanceof Error ? e.message : String(e)); + } + }} + > + + + ) : null, }, ]; @@ -398,6 +427,50 @@ export default function KnowledgeBasesPage() { + + { + setEditDocModalOpen(false); + setEditingDoc(null); + }} + confirmLoading={editDocSaving} + onOk={async () => { + if (!drawerKb || !editingDoc) return; + const values = await editDocForm.validateFields(); + setEditDocSaving(true); + try { + await request(`/admin/knowledge-bases/${drawerKb.id}/documents/${editingDoc.id}`, { + method: 'PUT', + body: JSON.stringify({ + title: values.title, + contentText: values.contentText || null, + }), + }); + message.success('已保存'); + setEditDocModalOpen(false); + setEditingDoc(null); + void loadDocs(drawerKb); + reload(); + } catch (e) { + message.error(e instanceof Error ? e.message : String(e)); + } finally { + setEditDocSaving(false); + } + }} + destroyOnClose + width={640} + > +
+ + + + + + +
+
); } diff --git a/apps/admin-web/src/pages/SupportTicketsPage.tsx b/apps/admin-web/src/pages/SupportTicketsPage.tsx index 0be5414..f192f84 100644 --- a/apps/admin-web/src/pages/SupportTicketsPage.tsx +++ b/apps/admin-web/src/pages/SupportTicketsPage.tsx @@ -34,6 +34,7 @@ import { type BatchReviewPreviewItem, type BatchReviewPreviewResponse, type DevPlanTaskTypeDto, + type DevPlanVersionDto, type SupportTicketDto, type SupportTicketLinkedTaskDto, type SupportTicketStatusDto, @@ -96,6 +97,10 @@ export default function SupportTicketsPage() { const [batchConfirming, setBatchConfirming] = useState(false); const [batchStatusOpen, setBatchStatusOpen] = useState(false); const [batchStatusSaving, setBatchStatusSaving] = useState(false); + const [batchCreateSaving, setBatchCreateSaving] = useState(false); + const [batchPublishOpen, setBatchPublishOpen] = useState(false); + const [batchPublishSaving, setBatchPublishSaving] = useState(false); + const [versions, setVersions] = useState([]); const [editOpen, setEditOpen] = useState(false); const [editSaving, setEditSaving] = useState(false); const [reviewDecision, setReviewDecision] = useState<'APPROVE' | 'REJECT'>('APPROVE'); @@ -114,9 +119,21 @@ export default function SupportTicketsPage() { rejectReason?: string; note?: string; }>(); + const [batchPublishForm] = Form.useForm<{ + versionId: string; + dispatchToWecom?: boolean; + dispatchSupplement?: string; + }>(); const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN'; + useEffect(() => { + if (!batchPublishOpen) return; + request<{ items: DevPlanVersionDto[] }>('/admin/dev-plan/versions?pageSize=100') + .then((res) => setVersions(res.items ?? [])) + .catch(() => setVersions([])); + }, [batchPublishOpen]); + useEffect(() => { request('/admin/auth/me').then(setProfile).catch(() => {}); }, []); @@ -257,6 +274,67 @@ export default function SupportTicketsPage() { } } + async function submitBatchCreateTasks() { + if (!selectedRowKeys.length) return; + setBatchCreateSaving(true); + try { + const res = await request<{ successCount: number; failCount: number }>( + '/admin/support-tickets/batch-create-tasks', + { + method: 'POST', + body: JSON.stringify({ ticketIds: selectedRowKeys }), + }, + ); + message.success(`已创建 ${res.successCount} 条任务,跳过/失败 ${res.failCount} 条`); + setSelectedRowKeys([]); + reload(); + } catch (e) { + message.error(e instanceof Error ? e.message : '批量创建任务失败'); + } finally { + setBatchCreateSaving(false); + } + } + + function openBatchPublish() { + batchPublishForm.setFieldsValue({ + versionId: undefined, + dispatchToWecom: localStorage.getItem(DISPATCH_WECOM_STORAGE_KEY) !== 'false', + dispatchSupplement: DEFAULT_DISPATCH_SUPPLEMENT, + }); + setBatchPublishOpen(true); + } + + async function submitBatchPublish() { + const values = await batchPublishForm.validateFields(); + localStorage.setItem(DISPATCH_WECOM_STORAGE_KEY, values.dispatchToWecom ? 'true' : 'false'); + setBatchPublishSaving(true); + try { + const res = await request<{ + successCount: number; + failCount: number; + linkedTaskCount: number; + }>('/admin/support-tickets/batch-publish', { + method: 'POST', + body: JSON.stringify({ + ticketIds: selectedRowKeys, + versionId: values.versionId, + dispatchToWecom: !!values.dispatchToWecom, + dispatchSupplement: values.dispatchSupplement?.trim() || undefined, + }), + }); + message.success( + `已关联 ${res.linkedTaskCount} 条任务到版本,工单成功 ${res.successCount} 条,失败 ${res.failCount} 条`, + ); + setBatchPublishOpen(false); + setSelectedRowKeys([]); + reload(); + } catch (e) { + message.error(e instanceof Error ? e.message : '批量发布失败'); + } finally { + setBatchPublishSaving(false); + } + } + async function submitCreateTasks() { const values = await tasksForm.validateFields(); const note = reviewForm.getFieldValue('note') as string | undefined; @@ -440,6 +518,16 @@ export default function SupportTicketsPage() { {isSuperAdmin ? ( <> + + @@ -945,6 +1033,33 @@ export default function SupportTicketsPage() { + setBatchPublishOpen(false)} + onOk={() => void submitBatchPublish()} + confirmLoading={batchPublishSaving} + destroyOnClose + > + + 已选 {selectedRowKeys.length} 条工单。将把各工单关联的开发任务追加到所选版本;无任务的工单将跳过。 + +
+ +