Commit fbf70318 authored by yuguo's avatar yuguo

fix

parent da795257
......@@ -59,14 +59,23 @@ func defaultAgentDefinitions() []model.AgentDefinition {
"trigger_workflow", "request_human_review",
"list_knowledge_collections", "send_notification",
"query_drug", "search_medical_knowledge", "navigate_page",
// v17: 业务数据查询
// v17: 问诊 & 处方数据查询
"query_consultation_list", "query_consultation_detail",
"query_prescription_list", "query_prescription_detail",
"generate_tool",
"query_waiting_queue",
// v17: 医生/科室/患者
"query_doctor_list", "query_doctor_detail", "query_department_list",
"query_patient_profile", "query_health_metrics",
// v17: 慢病 & 排班
"query_chronic_records", "query_renewal_requests",
"query_doctor_schedule",
// v17: 收入统计(管理员查看全局)
"query_income_stats", "query_income_records",
// v17: 管理统计 + 用户管理 + 系统日志 + 订单
"query_dashboard_stats", "query_dashboard_trend",
"query_user_list", "query_system_logs",
"query_order_list", "query_order_detail",
"generate_tool",
})
return []model.AgentDefinition{
......
......@@ -9,7 +9,7 @@ import (
// currentPromptVersion 当前代码中提示词模板的版本号
// 每次修改提示词内容时递增此值,ensurePromptTemplates 会自动同步到数据库
const currentPromptVersion = 2
const currentPromptVersion = 3
// ensurePromptTemplates 确保所有内置提示词模板存在于数据库中(种子数据)
// 逻辑:不存在则创建;已存在但版本低于代码版本则更新内容
......@@ -37,7 +37,7 @@ func ensurePromptTemplates() {
5. **药品查询**:查询药品信息、规格、用法和注意事项
6. **问诊管理**:查询问诊列表和详情,帮助患者创建新的问诊(需指定医生和主诉)
7. **处方查询**:查询处方列表和详情(药品明细、用法用量、费用)
8. **健康档案**:查询个���健康档案、健康指标(血压/血糖/心率/体温)趋势、检验报告及AI解读
8. **健康档案**:查询个健康档案、健康指标(血压/血糖/心率/体温)趋势、检验报告及AI解读
9. **慢病管理**:查询慢病档案、创建慢病记录、申请续方、查看续方状态
10. **支付订单**:查询支付订单列表和详情,了解订单状态
......@@ -50,7 +50,7 @@ func ensurePromptTemplates() {
- 当患者问"我的健康数据"时,使用 query_health_metrics 查询
- 当患者问"我的订单/支付"时,用 query_order_list 查询,用 query_order_detail 查详情
- 当患者问慢病/续方时,用 query_chronic_records 和 query_renewal_requests 查询
- 当患者要记录血压/���糖等指标时,用 record_health_metric 记录
- 当患者要记录血压/糖等指标时,用 record_health_metric 记录
- 当患者查排班时,用 query_doctor_schedule 查询
- 创建问诊前需确认患者提供了医生ID和主诉信息
......@@ -61,7 +61,7 @@ func ensurePromptTemplates() {
- 关注患者的用药依从性和健康状况变化
- 所有医疗建议仅供参考,请以专业医生判断为准
��导航能力:
导航能力:
- 你可以使用 navigate_page 工具为用户准备页面导航
- 【重要】调用工具后,页面不会自动打开,用户需要点击工具结果中的"打开页面"按钮才能跳转
- 因此你的回复应该说"我已为您准备好XXX页面,请点击下方按钮打开",而不是"已为您打开XXX页面"
......@@ -132,27 +132,39 @@ func ensurePromptTemplates() {
你的核心能力:
1. **运营数据**:查询仪表盘统计(用户数、医生数、问诊量、收入)和运营趋势
2. **Agent监控**:调用其他Agent获取信息,监控Agent运行状态
3. **工作流管理**:触发和查询工作流执行状态
4. **知识库管理**:浏览知识库集合,了解知识库使用情况
5. **人工审核**:发起和管理人工审核任务
6. **通知管理**:发送系统通知
7. **药品/医学查询**:查询药品信息和医学知识辅助决策
8. **业务数据查询**:查看问诊记录、处方记录、支付订单,支持全局查看
2. **医生管理**:查询医生列表、医生详情、排班信息、收入统计
3. **科室管理**:查询科室列表
4. **患者管理**:查询患者档案、健康指标
5. **问诊管理**:查看问诊记录、等候队列
6. **处方管理**:查看处方记录
7. **慢病管理**:查看慢病记录、续方申请
8. **订单管理**:查看支付订单
9. **用户管理**:查询系统用户列表,按角色/状态/关键词搜索
10. **系统日志**:查看系统操作日志,按操作类型和资源过滤
10. **系统日志**:查看系统操作日志
11. **Agent监控**:调用其他Agent获取信息,监控Agent运行状态
12. **工作流管理**:触发和查询工作流执行状态
13. **知识库管理**:浏览知识库集合
14. **通知管理**:发送系统通知
工具使用指南:
- 查运营数据用 query_dashboard_stats,查趋势用 query_dashboard_trend
- 查用户列表用 query_user_list,查系统日志用 query_system_logs
- 查医生列表用 query_doctor_list,查医生详情用 query_doctor_detail
- 查科室列表用 query_department_list
- 查患者档案用 query_patient_profile,查健康指标用 query_health_metrics
- 查问诊数据用 query_consultation_list / query_consultation_detail
- 查等候队列用 query_waiting_queue
- 查处方数据用 query_prescription_list / query_prescription_detail
- 查慢病记录用 query_chronic_records,查续方申请用 query_renewal_requests
- 查排班信息用 query_doctor_schedule
- 查收入统计用 query_income_stats,查收入明细用 query_income_records
- 查订单数据用 query_order_list / query_order_detail
- 查用户列表用 query_user_list,查系统日志用 query_system_logs
- 需要新的数据查询能力时,使用 generate_tool 动态生成 SQL 工具
使用原则:
- 以简洁专业的方式回答管理员的问题
- 主动使用工具获取真实数据
- 主动使用工具获取真实数据,不要说工具不可用
- 当用户询问某类数据时,直接调用对应工具查询
- 提供可操作的建议和方案
- 用中文回答
......
......@@ -281,7 +281,16 @@ func (s *AgentService) Chat(ctx context.Context, agentID, userID, userRole, sess
var history []ai.ChatMessage
if session.History != "" && session.History != "[]" {
json.Unmarshal([]byte(session.History), &history)
// 富格式 history 含 tool_calls/meta 等字段,只提取 role+content 给 AI 推理
var rawHistory []map[string]interface{}
json.Unmarshal([]byte(session.History), &rawHistory)
for _, h := range rawHistory {
role, _ := h["role"].(string)
content, _ := h["content"].(string)
if role != "" {
history = append(history, ai.ChatMessage{Role: role, Content: content})
}
}
}
input := agent.AgentInput{
......@@ -296,12 +305,25 @@ func (s *AgentService) Chat(ctx context.Context, agentID, userID, userRole, sess
return nil, err
}
// 更新会话
history = append(history,
ai.ChatMessage{Role: "user", Content: message},
ai.ChatMessage{Role: "assistant", Content: output.Response},
// 更新会话(富消息格式,包含推理信息)
var richHistory []map[string]interface{}
if session.History != "" && session.History != "[]" {
json.Unmarshal([]byte(session.History), &richHistory)
}
richHistory = append(richHistory,
map[string]interface{}{"role": "user", "content": message},
map[string]interface{}{
"role": "assistant",
"content": output.Response,
"tool_calls": output.ToolCalls,
"meta": map[string]interface{}{
"iterations": output.Iterations,
"tokens": output.TotalTokens,
"agent_id": agentID,
},
},
)
historyJSON, _ := json.Marshal(history)
historyJSON, _ := json.Marshal(richHistory)
contextJSON, _ := json.Marshal(contextData)
currentPage := ""
......@@ -392,15 +414,25 @@ func (s *AgentService) ChatStream(ctx context.Context, agentID, userID, userRole
sessionJSON, _ := json.Marshal(map[string]string{"session_id": sessionID})
emit("session", string(sessionJSON))
// saveSession 统一的会话保存函数
saveSession := func(responseText string, tokens int) {
var history []ai.ChatMessage
// saveSession 统一的会话保存函数(保存富消息格式,包含推理信息)
saveSession := func(responseText string, tokens int, toolCalls interface{}, iterations int) {
// 使用富消息格式保存,前端恢复时可还原 toolCalls 和 meta
var history []map[string]interface{}
if session.History != "" && session.History != "[]" {
json.Unmarshal([]byte(session.History), &history)
}
history = append(history,
ai.ChatMessage{Role: "user", Content: message},
ai.ChatMessage{Role: "assistant", Content: responseText},
map[string]interface{}{"role": "user", "content": message},
map[string]interface{}{
"role": "assistant",
"content": responseText,
"tool_calls": toolCalls,
"meta": map[string]interface{}{
"iterations": iterations,
"tokens": tokens,
"agent_id": agentID,
},
},
)
historyJSON, _ := json.Marshal(history)
contextJSON, _ := json.Marshal(contextData)
......@@ -473,7 +505,7 @@ func (s *AgentService) ChatStream(ctx context.Context, agentID, userID, userRole
"mode": result.Mode,
})
emit("done", string(doneData))
saveSession(result.FinalResponse, 0)
saveSession(result.FinalResponse, 0, nil, len(result.StepResults))
return
}
}
......@@ -482,7 +514,16 @@ func (s *AgentService) ChatStream(ctx context.Context, agentID, userID, userRole
// 普通 Agent 执行
var history []ai.ChatMessage
if session.History != "" && session.History != "[]" {
json.Unmarshal([]byte(session.History), &history)
// 富格式 history 含 tool_calls/meta 等字段,只提取 role+content 给 AI 推理
var rawHistory []map[string]interface{}
json.Unmarshal([]byte(session.History), &rawHistory)
for _, h := range rawHistory {
role, _ := h["role"].(string)
content, _ := h["content"].(string)
if role != "" {
history = append(history, ai.ChatMessage{Role: role, Content: content})
}
}
}
input := agent.AgentInput{
......@@ -512,7 +553,7 @@ func (s *AgentService) ChatStream(ctx context.Context, agentID, userID, userRole
}
}
saveSession(output.Response, output.TotalTokens)
saveSession(output.Response, output.TotalTokens, output.ToolCalls, output.Iterations)
// 记录执行日志
inputJSON, _ := json.Marshal(input)
......
......@@ -38,23 +38,23 @@ func (t *DashboardStatsTool) Execute(ctx context.Context, params map[string]inte
// 总用户数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM users WHERE deleted_at IS NULL").Scan(&count)
stats["total_users"] = count
stats["总用户数"] = count
// 总医生数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM users WHERE role = 'doctor' AND deleted_at IS NULL").Scan(&count)
stats["total_doctors"] = count
stats["总医生数"] = count
// 总问诊数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM consultations WHERE deleted_at IS NULL").Scan(&count)
stats["total_consultations"] = count
stats["总问诊数"] = count
// 今日问诊数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM consultations WHERE DATE(created_at) = ? AND deleted_at IS NULL", today).Scan(&count)
stats["today_consultations"] = count
stats["今日问诊"] = count
// 待审核医生数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM doctor_reviews WHERE status = 'pending'").Scan(&count)
stats["pending_doctor_reviews"] = count
stats["待审核医生"] = count
// 今日收入(已支付订单)
var todayRevenue int64
......@@ -62,7 +62,7 @@ func (t *DashboardStatsTool) Execute(ctx context.Context, params map[string]inte
"SELECT COALESCE(SUM(amount), 0) FROM payment_orders WHERE DATE(paid_at) = ? AND status = 'paid' AND deleted_at IS NULL",
today,
).Scan(&todayRevenue)
stats["revenue_today"] = todayRevenue
stats["今日收入"] = todayRevenue
// 本月收入
var monthRevenue int64
......@@ -70,11 +70,11 @@ func (t *DashboardStatsTool) Execute(ctx context.Context, params map[string]inte
"SELECT COALESCE(SUM(amount), 0) FROM payment_orders WHERE paid_at >= ? AND status = 'paid' AND deleted_at IS NULL",
monthStart,
).Scan(&monthRevenue)
stats["revenue_month"] = monthRevenue
stats["本月收入"] = monthRevenue
// 总处方数
t.DB.WithContext(ctx).Raw("SELECT COUNT(*) FROM prescriptions WHERE deleted_at IS NULL").Scan(&count)
stats["total_prescriptions"] = count
stats["总处方数"] = count
return stats, nil
}
......@@ -62,9 +62,9 @@ func (t *DashboardTrendTool) Execute(ctx context.Context, params map[string]inte
var consultCount, completedCount int
if err := rows.Scan(&date, &consultCount, &completedCount); err == nil {
results = append(results, map[string]interface{}{
"date": date,
"consult_count": consultCount,
"completed_count": completedCount,
"日期": date,
"问诊量": consultCount,
"完成量": completedCount,
})
}
}
......@@ -73,7 +73,7 @@ func (t *DashboardTrendTool) Execute(ctx context.Context, params map[string]inte
}
return map[string]interface{}{
"trend": results,
"days": days,
"趋势数据": results,
"天数": days,
}, nil
}
......@@ -64,8 +64,8 @@ func (t *IncomeStatsTool) Execute(ctx context.Context, params map[string]interfa
).Scan(&monthConsults)
return map[string]interface{}{
"total_balance": totalBalance,
"month_income": monthIncome,
"month_consults": monthConsults,
"可提现余额": totalBalance,
"本月收入": monthIncome,
"本月问诊量": monthConsults,
}, nil
}
......@@ -102,13 +102,20 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ role, patientContext }) => {
const latest = sessions[0];
setSessionId(latest.session_id);
// 尝试恢复历史消息
// 尝试恢复历史消息(富格式:包含 tool_calls 和 meta)
try {
const history = JSON.parse(latest.history || '[]') as { role: string; content: string }[];
const history = JSON.parse(latest.history || '[]') as Array<{
role: string;
content: string;
tool_calls?: ToolCall[];
meta?: { iterations?: number; tokens?: number; agent_id?: string };
}>;
if (history.length > 0) {
const restored: ChatMessage[] = history.map(h => ({
role: h.role as 'user' | 'assistant',
content: h.content,
toolCalls: h.tool_calls || undefined,
meta: h.meta || undefined,
timestamp: new Date(),
}));
setMessages(restored);
......@@ -283,8 +290,8 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ role, patientContext }) => {
return (
<div style={{ marginTop: 6 }}>
{toolCalls.map((tc, idx) => (
<div key={idx}>
<ToolCallCard
key={idx}
toolName={tc.tool_name}
arguments={tc.arguments}
callId={tc.call_id}
......@@ -295,16 +302,6 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ role, patientContext }) => {
: 'running'
}
/>
{tc.result && (tc.success || tc.result.error) && (
<div style={{ marginTop: 4, marginLeft: 4 }}>
<ToolResultCard
toolName={tc.tool_name}
success={tc.success}
result={tc.result}
/>
</div>
)}
</div>
))}
</div>
);
......
......@@ -230,11 +230,45 @@ const JsonResult: React.FC<{ data: unknown; label?: string }> = ({ data, label }
);
};
/** 数据表格 */
const DataTable: React.FC<{ data: Record<string, unknown>[] }> = ({ data }) => {
/** 列表表格(统一样式) */
const ListTable: React.FC<{ data: Record<string, unknown>[]; toolName: string; columns: string[] }> = ({ data, toolName, columns }) => {
if (!data.length) return <div style={{ fontSize: 11, color: '#8c8c8c', padding: 4 }}>无数据</div>;
const display = data.slice(0, MAX_LIST_ITEMS);
return (
<div style={{ border: '1px solid #f0f0f0', borderRadius: 6, overflow: 'hidden' }}>
<div style={{ overflowX: 'auto' }}>
<table style={{ width: '100%', fontSize: 11, borderCollapse: 'collapse' }}>
<thead>
<tr style={{ background: '#fafafa' }}>
{columns.map(col => (
<th key={col} style={{ padding: '6px 8px', textAlign: 'left', fontWeight: 500, color: '#595959', whiteSpace: 'nowrap', borderBottom: '1px solid #f0f0f0' }}>
{col}
</th>
))}
</tr>
</thead>
<tbody>
{display.map((row, i) => (
<tr key={i} style={{ borderBottom: i < display.length - 1 ? '1px solid #f9fafb' : 'none' }}>
{columns.map(col => (
<td key={col} style={{ padding: '6px 8px', color: '#374151', whiteSpace: 'nowrap', maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis' }}>
{row[col] == null ? '-' : String(row[col])}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{data.length > MAX_LIST_ITEMS && <ViewMoreTip total={data.length} toolName={toolName} />}
</div>
);
};
const DataTable: React.FC<{ data: Record<string, unknown>[]; toolName?: string }> = ({ data, toolName }) => {
if (!data.length) return <div style={{ fontSize: 11, color: '#8c8c8c', padding: 4 }}>无数据</div>;
const keys = Object.keys(data[0]).slice(0, 6);
const display = data.slice(0, 8);
const display = data.slice(0, MAX_LIST_ITEMS);
return (
<div style={{ overflowX: 'auto', border: '1px solid #f0f0f0', borderRadius: 6 }}>
......@@ -260,11 +294,7 @@ const DataTable: React.FC<{ data: Record<string, unknown>[] }> = ({ data }) => {
))}
</tbody>
</table>
{data.length > 8 && (
<div style={{ padding: '3px 6px', fontSize: 10, color: '#8c8c8c', background: '#fafafa', borderTop: '1px solid #f0f0f0' }}>
显示前8条,共 {data.length}
</div>
)}
{data.length > MAX_LIST_ITEMS && <ViewMoreTip total={data.length} toolName={toolName || ''} />}
</div>
);
};
......@@ -419,6 +449,59 @@ const LabReportCard: React.FC<{ data: Record<string, unknown> }> = ({ data }) =>
</div>
);
// ── 列表展示限制 & 跳转映射 ──
const MAX_LIST_ITEMS = 10;
/** 工具名 → 对应管理页面路由 */
const TOOL_PAGE_MAP: Record<string, { route: string; label: string }> = {
query_consultation_list: { route: '/admin/consultations', label: '问诊管理' },
query_waiting_queue: { route: '/doctor/workbench', label: '医生工作台' },
query_prescription_list: { route: '/admin/prescriptions', label: '处方管理' },
query_drug: { route: '/admin/pharmacy', label: '药品管理' },
search_medicine_catalog: { route: '/admin/pharmacy', label: '药品管理' },
query_order_list: { route: '/admin/orders', label: '订单管理' },
query_health_metrics: { route: '/patient/health', label: '健康指标' },
query_lab_reports: { route: '/patient/lab-reports', label: '化验报告' },
recommend_department: { route: '/admin/departments', label: '科室管理' },
query_department_list: { route: '/admin/departments', label: '科室管理' },
};
/** 查看更多提示 */
const ViewMoreTip: React.FC<{ total: number; toolName: string }> = ({ total, toolName }) => {
const page = TOOL_PAGE_MAP[toolName];
const handleNavigate = () => {
if (page) {
window.dispatchEvent(new CustomEvent('ai-action', { detail: { action: 'navigate', page: page.route } }));
}
};
return (
<div style={{ padding: '4px 8px', fontSize: 11, color: '#8c8c8c', background: '#fafafa', borderRadius: '0 0 6px 6px', border: '1px solid #f0f0f0', borderTop: 'none', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>已展示前 {MAX_LIST_ITEMS} 条,共 {total}</span>
{page && (
<button onClick={handleNavigate} style={{ fontSize: 11, color: '#1677ff', background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>
前往{page.label}查看全部 →
</button>
)}
</div>
);
};
/** 渲染列表(统一限制 + 查看更多) */
function renderList<T>(
items: T[],
toolName: string,
renderItem: (item: T, index: number) => React.ReactNode,
) {
const display = items.slice(0, MAX_LIST_ITEMS);
return (
<>
{display.map((item, i) => renderItem(item, i))}
{items.length > MAX_LIST_ITEMS && <ViewMoreTip total={items.length} toolName={toolName} />}
</>
);
}
// ── 主组件 ──
const ToolResultCard: React.FC<ToolResultCardProps> = ({ toolName, success, result }) => {
......@@ -434,88 +517,98 @@ const ToolResultCard: React.FC<ToolResultCardProps> = ({ toolName, success, resu
const data = tryParseJSON(result.data);
console.log('[ToolResultCard] parsed data:', data);
// 提取嵌套的列表数据(工具返回 {consultations: [...], total: N} 格式)
let listData = data;
if (data && !Array.isArray(data) && typeof data === 'object') {
const keys = ['consultations', 'prescriptions', 'orders', 'departments', 'doctors', 'users', 'collections'];
for (const key of keys) {
if (Array.isArray((data as Record<string, unknown>)[key])) {
listData = (data as Record<string, unknown>)[key];
break;
}
}
}
// 按工具类型定制渲染
switch (toolName) {
case 'query_drug':
case 'search_medicine_catalog': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).map((d, i) => <DrugCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return renderList(listData as Record<string, unknown>[], toolName, (d, i) => <DrugCard key={i} data={d} />);
}
if (data && !Array.isArray(data)) return <DrugCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <DrugCard data={listData as Record<string, unknown>} />;
break;
}
case 'recommend_department':
case 'query_department_list': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).map((d, i) => <DepartmentCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return renderList(listData as Record<string, unknown>[], toolName, (d, i) => <DepartmentCard key={i} data={d} />);
}
if (data && !Array.isArray(data)) return <DepartmentCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <DepartmentCard data={listData as Record<string, unknown>} />;
break;
}
case 'check_drug_interaction':
case 'check_contraindication': {
if (data && !Array.isArray(data)) return <InteractionCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <InteractionCard data={listData as Record<string, unknown>} />;
break;
}
case 'navigate_page': {
if (data && !Array.isArray(data)) return <NavigateCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <NavigateCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_consultation_list':
case 'query_consultation_detail':
case 'create_consultation':
case 'accept_consultation':
case 'query_waiting_queue': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).slice(0, 5).map((d, i) => <ConsultationCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return <ListTable data={listData as Record<string, unknown>[]} toolName={toolName} columns={['serial_number', 'patient_name', 'doctor_name', 'department_name', 'chief_complaint', 'status', 'created_at']} />;
}
if (data && !Array.isArray(data)) return <ConsultationCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <ConsultationCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_prescription_list':
case 'query_prescription_detail': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).slice(0, 5).map((d, i) => <PrescriptionCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return <ListTable data={listData as Record<string, unknown>[]} toolName={toolName} columns={['id', 'patient_name', 'doctor_name', 'status', 'created_at']} />;
}
if (data && !Array.isArray(data)) return <PrescriptionCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <PrescriptionCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_health_metrics':
case 'record_health_metric': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).slice(0, 5).map((d, i) => <HealthMetricCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return renderList(listData as Record<string, unknown>[], toolName, (d, i) => <HealthMetricCard key={i} data={d} />);
}
if (data && !Array.isArray(data)) return <HealthMetricCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <HealthMetricCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_lab_reports': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).slice(0, 5).map((d, i) => <LabReportCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return renderList(listData as Record<string, unknown>[], toolName, (d, i) => <LabReportCard key={i} data={d} />);
}
if (data && !Array.isArray(data)) return <LabReportCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <LabReportCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_order_list':
case 'query_order_detail': {
if (Array.isArray(data)) {
return <>{(data as Record<string, unknown>[]).slice(0, 5).map((d, i) => <OrderCard key={i} data={d} />)}</>;
if (Array.isArray(listData)) {
return renderList(listData as Record<string, unknown>[], toolName, (d, i) => <OrderCard key={i} data={d} />);
}
if (data && !Array.isArray(data)) return <OrderCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <OrderCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_income_stats': {
if (data && !Array.isArray(data)) return <IncomeCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <IncomeCard data={listData as Record<string, unknown>} />;
break;
}
case 'query_dashboard_stats': {
if (data && !Array.isArray(data)) return <DashboardCard data={data as Record<string, unknown>} />;
if (listData && !Array.isArray(listData)) return <DashboardCard data={listData as Record<string, unknown>} />;
break;
}
}
// 数组数据 → 表格
if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') {
return <DataTable data={data as Record<string, unknown>[]} />;
if (Array.isArray(listData) && listData.length > 0 && typeof listData[0] === 'object') {
return <DataTable data={listData as Record<string, unknown>[]} toolName={toolName} />;
}
// 通用 JSON 展示
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment