feat: 完善环境规则、会诊治理、知识审核与效果评估

This commit is contained in:
weijuesen
2026-08-14 10:33:17 +08:00
parent 69ef5a0704
commit 126c215a6b
27 changed files with 963 additions and 88 deletions
+7 -1
View File
@@ -11,6 +11,12 @@ export interface Consultation {
snapshot?: any;
status: string;
expertId?: string;
assigneeId?: string;
assignedAt?: string;
acceptedAt?: string;
needsInfoAt?: string;
slaDeadline?: string;
overdueAt?: string;
opinion?: string;
plan?: string;
resolvedAt?: string;
@@ -25,6 +31,6 @@ export const createConsultation = (data: Partial<Consultation>) =>
post<Consultation>('/consultations', data);
export const updateConsultation = (id: string, data: Partial<Consultation>) =>
patch<Consultation>(`/consultations/${id}`, data);
export const resolveConsultation = (id: string, data: { opinion: string; plan: string }) =>
export const resolveConsultation = (id: string, data: { opinion: string; plan: string; reason?: string }) =>
post<Consultation>(`/consultations/${id}/resolve`, data);
export const archiveConsultation = (id: string) => post<Consultation>(`/consultations/${id}/archive`);
+14
View File
@@ -11,9 +11,23 @@ export interface HealthProfile {
batch?: any;
}
export interface EffectReport {
eventId: string;
beforeRiskAvg?: number;
afterRiskAvg?: number;
beforePositiveRate?: number;
afterPositiveRate?: number;
recurrences: number;
missing: string[];
conclusion: string;
}
export const getHealthProfile = (roomId: string) =>
get<HealthProfile>(`/health-profiles/${roomId}`);
export const getRoomEffect = (roomId: string, params?: any) =>
get<EffectReport>(`/health-profiles/${roomId}/effect`, { params });
export const getMonthlyStats = (year?: number) =>
get<{ year: number; stats: { month: string; total: number; diseases: Record<string, number> }[] }>(
'/trace-records/monthly-stats',
+8
View File
@@ -17,6 +17,10 @@ export interface Disease {
detection?: string;
prevention?: string;
imageUrl?: string;
status?: string;
source?: string;
sourceDate?: string;
expertConfirmedBy?: string;
sortOrder?: number;
enabled?: boolean;
createdAt?: string;
@@ -29,6 +33,10 @@ export interface KnowledgeArticle {
title: string;
summary?: string;
content?: string;
status?: string;
source?: string;
sourceDate?: string;
expertConfirmedBy?: string;
sortOrder?: number;
enabled?: boolean;
createdAt?: string;
+19 -5
View File
@@ -14,8 +14,10 @@ import {
import { authService } from '../services/auth';
const STATUS_LABELS: Record<string, { color: string; text: string }> = {
pending: { color: 'gold', text: '待会诊' },
consulting: { color: 'blue', text: '会诊中' },
unassigned: { color: 'gold', text: '待分派' },
assigned: { color: 'blue', text: '已分派' },
accepted: { color: 'cyan', text: '已受理' },
needs_info: { color: 'purple', text: '待补充' },
resolved: { color: 'green', text: '已出方案' },
archived: { color: 'default', text: '已归档' },
};
@@ -53,11 +55,23 @@ export default function ConsultationsPage() {
</a>,
...(canWrite()
? [
r.status === 'pending' ? (
r.status === 'unassigned' ? (
<a
key="take"
onClick={async () => {
await updateConsultation(r.id, { status: 'consulting' });
await updateConsultation(r.id, { status: 'assigned' });
message.success('已受理');
actionRef.current?.reload();
}}
>
</a>
) : null,
r.status === 'assigned' ? (
<a
key="accept"
onClick={async () => {
await updateConsultation(r.id, { status: 'accepted' });
message.success('已受理');
actionRef.current?.reload();
}}
@@ -65,7 +79,7 @@ export default function ConsultationsPage() {
</a>
) : null,
r.status === 'pending' || r.status === 'consulting' ? (
r.status === 'accepted' || r.status === 'needs_info' || r.status === 'assigned' ? (
<a
key="resolve"
onClick={() => {
+10
View File
@@ -33,6 +33,13 @@ const KIND_LABELS: Record<string, string> = {
seasonal_tip: '季节性提醒',
};
const STATUS_LABELS: Record<string, { color: string; text: string }> = {
draft: { color: 'gold', text: '草稿' },
review: { color: 'blue', text: '审核中' },
published: { color: 'green', text: '已发布' },
withdrawn: { color: 'default', text: '已撤回' },
};
const canWrite = () => authService.hasPermission('knowledge:write');
function DiseaseTab() {
@@ -63,6 +70,7 @@ function DiseaseTab() {
width: 80,
render: (_, r) => (r.enabled === false ? <Tag color="default"></Tag> : <Tag color="green"></Tag>),
},
{ title: '审核状态', dataIndex: 'status', search: false, render: (_, r) => (r.status ? <Tag color={STATUS_LABELS[r.status]?.color}>{STATUS_LABELS[r.status]?.text || r.status}</Tag> : '-') },
{
title: '操作',
valueType: 'option',
@@ -254,6 +262,8 @@ function ArticleTab() {
render: (_, r) => <Tag>{KIND_LABELS[r.kind] || r.kind}</Tag>,
},
{ title: '摘要', dataIndex: 'summary', search: false, ellipsis: true, render: (_, r) => r.summary || '-' },
{ title: '审核状态', dataIndex: 'status', search: false, render: (_, r) => (r.status ? <Tag color={STATUS_LABELS[r.status]?.color}>{STATUS_LABELS[r.status]?.text || r.status}</Tag> : '-') },
{ title: '来源', dataIndex: 'source', search: false, render: (_, r) => r.source || '-' },
{ title: '排序', dataIndex: 'sortOrder', search: false, width: 70 },
{
title: '操作',
+37
View File
@@ -20,6 +20,7 @@ import {
type RegionStat,
} from '../dal/trace';
import { getMonthlyStats } from '../dal/health';
import { getRoomEffect, type EffectReport } from '../dal/health';
import { authService } from '../services/auth';
const STATUS_LABELS: Record<string, { color: string; text: string }> = {
@@ -40,6 +41,8 @@ export default function TracesPage() {
const [detail, setDetail] = useState<TraceRecord | null>(null);
const [checklist, setChecklist] = useState<ChecklistItem[]>([]);
const [answers, setAnswers] = useState<Record<string, string>>({});
const [effect, setEffect] = useState<EffectReport | null>(null);
const [effectRoomId, setEffectRoomId] = useState('');
useEffect(() => {
getRegionStats(90)
@@ -94,6 +97,12 @@ export default function TracesPage() {
setAnswers({});
};
const openEffect = async (r: TraceRecord) => {
if (!r.roomId) return;
setEffectRoomId(r.roomId);
setEffect(await getRoomEffect(r.roomId).catch(() => null));
};
const columns: ProColumns<TraceRecord>[] = [
{ title: '序号', valueType: 'indexBorder', search: false, width: 60 },
{ title: '病种', dataIndex: 'disease' },
@@ -126,6 +135,11 @@ export default function TracesPage() {
<a key="view" onClick={() => openDetail(r)}>
</a>,
r.roomId ? (
<a key="effect" onClick={() => openEffect(r)}>
</a>
) : null,
...(canWrite()
? [
r.status === 'pending' ? (
@@ -242,6 +256,29 @@ export default function TracesPage() {
</Form>
</Modal>
<Modal
title={`防控效果报告:${effectRoomId}`}
open={!!effect}
onCancel={() => setEffect(null)}
footer={<Button onClick={() => setEffect(null)}></Button>}
>
{effect ? (
<div>
<Typography.Paragraph>
<b>{effect.conclusion}</b>
<br />
{effect.beforeRiskAvg !== undefined ? effect.beforeRiskAvg.toFixed(1) : '缺失'}
<br />
{effect.afterRiskAvg !== undefined ? effect.afterRiskAvg.toFixed(1) : '缺失'}
<br />
{effect.recurrences}
<br />
{effect.missing.join('、') || '无'}
</Typography.Paragraph>
</div>
) : null}
</Modal>
<Drawer title={`溯源详情 - ${detail?.disease || ''}`} open={!!detail} width={680} onClose={() => setDetail(null)}>
{detail ? (
<div>