| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import React, { useState, useEffect } from "react";
- import { Card, Descriptions, Button, message, Tag, Spin } from "antd";
- import { ArrowLeftOutlined, CopyOutlined } from "@ant-design/icons";
- import { useParams, useNavigate } from "react-router-dom";
- import { toolsCallLogApi } from "../services/api";
- import dayjs from "dayjs";
- const ToolsCallLogDetail = () => {
- const { id } = useParams();
- const navigate = useNavigate();
- const [loading, setLoading] = useState(false);
- const [data, setData] = useState(null);
- // 格式化时间戳为 YYYY-MM-DD HH:mm:ss
- const formatTimestamp = (timestamp) => {
- if (!timestamp) return "-";
- return dayjs(timestamp).format("YYYY-MM-DD HH:mm:ss");
- };
- // 计算耗时(秒)
- const calculateDuration = (callTime, finishTime) => {
- if (!callTime || !finishTime) return "-";
- return ((finishTime - callTime) / 1000).toFixed(2) + "s";
- };
- // 复制到剪贴板
- const copyToClipboard = async (text) => {
- try {
- await navigator.clipboard.writeText(text);
- message.success("复制成功");
- } catch (error) {
- message.error("复制失败");
- }
- };
- // 获取详情数据
- const fetchDetail = async () => {
- setLoading(true);
- try {
- const response = await toolsCallLogApi.getDetail(id);
- setData(response.data);
- } catch (error) {
- message.error("获取详情失败");
- console.error("获取工具调用日志详情失败:", error);
- } finally {
- setLoading(false);
- }
- };
- // 返回列表
- const handleBack = () => {
- navigate("/tools-call-log");
- };
- useEffect(() => {
- if (id) {
- fetchDetail();
- }
- }, [id]);
- if (loading) {
- return (
- <div style={{ padding: "24px", textAlign: "center" }}>
- <Spin size="large" />
- </div>
- );
- }
- if (!data) {
- return (
- <div style={{ padding: "24px" }}>
- <Button icon={<ArrowLeftOutlined />} onClick={handleBack}>
- 返回列表
- </Button>
- <div style={{ textAlign: "center", marginTop: "50px" }}>
- <p>未找到相关数据</p>
- </div>
- </div>
- );
- }
- return (
- <div style={{ padding: "24px" }}>
- <div style={{ marginBottom: "16px" }}>
- <Button icon={<ArrowLeftOutlined />} onClick={handleBack}>
- 返回列表
- </Button>
- </div>
- <Card title="工具调用日志详情">
- <Descriptions bordered column={2}>
- <Descriptions.Item label="用户名" span={1}>
- {data.user || "-"}
- </Descriptions.Item>
- <Descriptions.Item label="工具名称" span={1}>
- {data.mcp_tools_name || "-"}
- </Descriptions.Item>
- <Descriptions.Item label="Token" span={2}>
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <span style={{ wordBreak: "break-all" }}>{data.token || "-"}</span>
- {data.token && (
- <Button
- type="text"
- size="small"
- icon={<CopyOutlined />}
- onClick={() => copyToClipboard(data.token)}
- title="复制Token"
- />
- )}
- </div>
- </Descriptions.Item>
- <Descriptions.Item label="状态" span={1}>
- <Tag color={data.status === "success" ? "green" : "red"}>
- {data.status === "success" ? "成功" : "失败"}
- </Tag>
- </Descriptions.Item>
- <Descriptions.Item label="耗时" span={1}>
- {calculateDuration(data.call_timestamp, data.finish_timestamp)}
- </Descriptions.Item>
- <Descriptions.Item label="调用时间" span={1}>
- {formatTimestamp(data.call_timestamp)}
- </Descriptions.Item>
- <Descriptions.Item label="完成时间" span={1}>
- {formatTimestamp(data.finish_timestamp)}
- </Descriptions.Item>
- {data.fail_msg && (
- <Descriptions.Item label="失败原因" span={2}>
- <div style={{ color: "#ff4d4f", wordBreak: "break-word" }}>
- {data.fail_msg}
- </div>
- </Descriptions.Item>
- )}
- </Descriptions>
- </Card>
- {/* 请求参数 */}
- <Card title="请求参数" style={{ marginTop: "16px" }}>
- <div style={{ position: "relative" }}>
- <Button
- type="text"
- size="small"
- icon={<CopyOutlined />}
- onClick={() => copyToClipboard(data.request_params || "")}
- style={{ position: "absolute", top: "8px", right: "8px", zIndex: 1 }}
- title="复制请求参数"
- />
- <pre
- style={{
- background: "#f5f5f5",
- padding: "12px",
- borderRadius: "4px",
- overflow: "auto",
- maxHeight: "300px",
- margin: 0,
- fontSize: "12px",
- lineHeight: "1.4",
- }}
- >
- {data.request_params || "无请求参数"}
- </pre>
- </div>
- </Card>
- {/* 工具响应 */}
- <Card title="工具响应" style={{ marginTop: "16px" }}>
- <div style={{ position: "relative" }}>
- <Button
- type="text"
- size="small"
- icon={<CopyOutlined />}
- onClick={() => copyToClipboard(data.response || "")}
- style={{ position: "absolute", top: "8px", right: "8px", zIndex: 1 }}
- title="复制工具响应"
- />
- <pre
- style={{
- background: "#f5f5f5",
- padding: "12px",
- borderRadius: "4px",
- overflow: "auto",
- maxHeight: "300px",
- margin: 0,
- fontSize: "12px",
- lineHeight: "1.4",
- }}
- >
- {data.response || "无响应数据"}
- </pre>
- </div>
- </Card>
- </div>
- );
- };
- export default ToolsCallLogDetail;
|