fix(web): 统计页空列表接入真实 API + 运营待办去硬编码
- DoctorDashboard: 咨询消息接入 consultationApi.listSessions - NurseDashboard: 随访队列接入 followUpApi.listTasks - OperatorDashboard: 热门文章接入 articleApi.list - OperatorWorkbench: 5 条硬编码待办替换为 actionInboxApi 真实数据
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuthStore } from '../../../../stores/auth';
|
||||
import { actionInboxApi, type WorkbenchStats } from '../../../../api/health/actionInbox';
|
||||
import { actionInboxApi, type WorkbenchStats, type ActionItem } from '../../../../api/health/actionInbox';
|
||||
import { useStatsData } from '../../StatisticsDashboard/useStatsData';
|
||||
import {
|
||||
dashboardApi,
|
||||
@@ -9,10 +9,22 @@ import {
|
||||
type ArticleStatsResp,
|
||||
} from '../../../../api/health/dashboard';
|
||||
|
||||
const TYPE_ICON: Record<string, { icon: string; bg: string; color: string }> = {
|
||||
ai_suggestion: { icon: '🤖', bg: '#F0F9FF', color: '#0284C7' },
|
||||
alert: { icon: '⚠️', bg: '#FFF1F2', color: '#E11D48' },
|
||||
followup: { icon: '📋', bg: '#F0FDF4', color: '#16A34A' },
|
||||
data_anomaly: { icon: '📊', bg: '#F5F3FF', color: '#7C3AED' },
|
||||
};
|
||||
|
||||
const PRIORITY_LABEL: Record<string, string> = {
|
||||
urgent: '紧急', high: '高', medium: '中', low: '低',
|
||||
};
|
||||
|
||||
export default function OperatorWorkbench() {
|
||||
const navigate = useNavigate();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const [stats, setStats] = useState<WorkbenchStats | null>(null);
|
||||
const [actionItems, setActionItems] = useState<ActionItem[]>([]);
|
||||
const [pointsActivity, setPointsActivity] = useState<PointsActivityItem[]>([]);
|
||||
const [articleStats, setArticleStats] = useState<ArticleStatsResp | null>(null);
|
||||
const statsData = useStatsData();
|
||||
@@ -22,6 +34,10 @@ export default function OperatorWorkbench() {
|
||||
.then((s) => setStats(s ?? null))
|
||||
.catch(() => {});
|
||||
|
||||
actionInboxApi.list({ status: 'pending', page: 1, page_size: 5 })
|
||||
.then((r) => setActionItems(r.data))
|
||||
.catch(() => {});
|
||||
|
||||
dashboardApi.getPointsRecentActivity()
|
||||
.then((d) => setPointsActivity(d ?? []))
|
||||
.catch(() => {});
|
||||
@@ -42,14 +58,6 @@ export default function OperatorWorkbench() {
|
||||
{ label: '待审核订单', value: stats?.total_pending ?? 0, color: '#E11D48', trend: '', trendDir: 'down' },
|
||||
];
|
||||
|
||||
const todos = [
|
||||
{ icon: '🎁', bg: '#FFF1F2', color: '#E11D48', title: `审核 ${stats?.total_pending ?? 0} 笔积分兑换订单`, sub: 'AI 标记异常订单需确认', action: '紧急', path: '/health/points-orders' },
|
||||
{ icon: '📝', bg: '#F0FDF4', color: '#16A34A', title: '发布新科普文章', sub: `已发布 ${articleStats?.published ?? 0} 篇,草稿 ${articleStats?.draft ?? 0} 篇`, action: '发布', path: '/health/articles/new' },
|
||||
{ icon: '🎪', bg: '#F0F9FF', color: '#0284C7', title: '推送活动报名提醒', sub: '报名截止临近,需推广', action: '推送', path: '/health/offline-events' },
|
||||
{ icon: '📊', bg: '#F5F3FF', color: '#7C3AED', title: '整理上周运营周报', sub: '数据已就绪', action: '查看', path: '/health/statistics' },
|
||||
{ icon: '👥', bg: '#FFFBEB', color: '#D97706', title: '跟进沉默用户', sub: '7 天未上报体征,建议关怀', action: '跟进', path: '/health/patients' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* AI Hero Card */}
|
||||
@@ -98,22 +106,29 @@ export default function OperatorWorkbench() {
|
||||
<span style={{ fontSize: 11, color: '#2563EB', cursor: 'pointer' }} onClick={() => navigate('/health/action-inbox')}>全部 →</span>
|
||||
</div>
|
||||
<div>
|
||||
{todos.map((todo) => (
|
||||
<div
|
||||
key={todo.path}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 18px', borderBottom: '1px solid #F1F5F9', cursor: 'pointer', transition: 'all 0.15s' }}
|
||||
onClick={() => navigate(todo.path)}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.background = '#EFF6FF'; }}
|
||||
onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}
|
||||
>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 6, background: todo.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, flexShrink: 0 }}>{todo.icon}</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500 }}>{todo.title}</div>
|
||||
<div style={{ fontSize: 11, color: '#94A3B8' }}>{todo.sub}</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, padding: '3px 10px', borderRadius: 4, border: '1px solid #E2E8F0', color: '#475569', cursor: 'pointer', flexShrink: 0 }}>{todo.action}</div>
|
||||
</div>
|
||||
))}
|
||||
{actionItems.length === 0 ? (
|
||||
<div style={{ padding: 20, textAlign: 'center', color: '#94A3B8', fontSize: 13 }}>暂无待办事项</div>
|
||||
) : (
|
||||
actionItems.map((item) => {
|
||||
const cfg = TYPE_ICON[item.action_type] ?? TYPE_ICON.alert;
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 18px', borderBottom: '1px solid #F1F5F9', cursor: 'pointer', transition: 'all 0.15s' }}
|
||||
onClick={() => navigate('/health/action-inbox')}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.background = '#EFF6FF'; }}
|
||||
onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}
|
||||
>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 6, background: cfg.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, flexShrink: 0 }}>{cfg.icon}</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500 }}>{item.title}</div>
|
||||
<div style={{ fontSize: 11, color: '#94A3B8' }}>{item.patient_name} · {item.summary}</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, padding: '3px 10px', borderRadius: 4, border: '1px solid #E2E8F0', color: '#475569', cursor: 'pointer', flexShrink: 0 }}>{PRIORITY_LABEL[item.priority] ?? '处理'}</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user