| | |
| | | import React from "react"; |
| | | import React, { useState } from "react"; |
| | | import { |
| | | FormDataConsumer, |
| | | NumberInput, |
| | | SelectInput, |
| | | TextInput, |
| | | useNotify, |
| | | useTranslate, |
| | | } from "react-admin"; |
| | | import { Grid, Typography } from "@mui/material"; |
| | | import { Alert, Button, Grid, Stack, Typography } from "@mui/material"; |
| | | import StatusSelectInput from "@/page/components/StatusSelectInput"; |
| | | import { validateDraftMcpConnectivity } from "@/api/ai/mcpMount"; |
| | | |
| | | const transportChoices = [ |
| | | { id: "SSE_HTTP", name: "SSE_HTTP" }, |
| | |
| | | { id: "BUILTIN", name: "BUILTIN" }, |
| | | ]; |
| | | |
| | | const AiMcpMountForm = ({ readOnly = false }) => ( |
| | | const AiMcpDraftTestSection = ({ formData, readOnly }) => { |
| | | const notify = useNotify(); |
| | | const translate = useTranslate(); |
| | | const [loading, setLoading] = useState(false); |
| | | const [result, setResult] = useState(null); |
| | | |
| | | const handleValidate = async () => { |
| | | setLoading(true); |
| | | try { |
| | | const data = await validateDraftMcpConnectivity(formData); |
| | | setResult(data); |
| | | notify(data?.message || translate("ai.mcp.connectivity.success")); |
| | | } catch (error) { |
| | | const nextResult = { |
| | | healthStatus: "UNHEALTHY", |
| | | message: error?.message || translate("ai.mcp.connectivity.failed"), |
| | | }; |
| | | setResult(nextResult); |
| | | notify(nextResult.message, { type: "error" }); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | |
| | | if (readOnly) { |
| | | return null; |
| | | } |
| | | |
| | | return ( |
| | | <> |
| | | <Grid item xs={12}> |
| | | <Stack direction="row" spacing={1} alignItems="center"> |
| | | <Button variant="outlined" onClick={handleValidate} disabled={loading}> |
| | | {loading ? translate("ai.common.testing") : translate("ai.mcp.form.testBeforeSave")} |
| | | </Button> |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate("ai.mcp.form.testDescription")} |
| | | </Typography> |
| | | </Stack> |
| | | </Grid> |
| | | {result && ( |
| | | <Grid item xs={12}> |
| | | <Alert severity={result.healthStatus === "HEALTHY" ? "success" : "error"}> |
| | | {result.message} |
| | | {result.initElapsedMs ? ` · ${result.initElapsedMs} ms` : ""} |
| | | {result.testedAt ? ` · ${result.testedAt}` : ""} |
| | | </Alert> |
| | | </Grid> |
| | | )} |
| | | </> |
| | | ); |
| | | }; |
| | | |
| | | const AiMcpMountForm = ({ readOnly = false }) => { |
| | | const translate = useTranslate(); |
| | | |
| | | return ( |
| | | <Grid container spacing={2} width={{ xs: "100%", xl: "80%" }}> |
| | | <Grid item xs={12}> |
| | | <Typography variant="h6">MCP 挂载配置</Typography> |
| | | <Typography variant="h6">{translate("ai.mcp.form.sections.main")}</Typography> |
| | | </Grid> |
| | | <Grid item xs={12} md={6}> |
| | | <TextInput source="name" label="名称" fullWidth disabled={readOnly} /> |
| | | <TextInput source="name" label="common.field.name" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12} md={6}> |
| | | <SelectInput source="transportType" label="传输类型" choices={transportChoices} fullWidth disabled={readOnly} /> |
| | | <SelectInput source="transportType" label="ai.mcp.fields.transportType" choices={transportChoices} fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <FormDataConsumer> |
| | | {({ formData }) => ( |
| | |
| | | <Grid item xs={12}> |
| | | <SelectInput |
| | | source="builtinCode" |
| | | label="内置 MCP" |
| | | label="ai.mcp.fields.builtinCode" |
| | | choices={[ |
| | | { id: "RSF_WMS", name: "RSF_WMS" }, |
| | | { id: "RSF_WMS_STOCK", name: "RSF_WMS_STOCK" }, |
| | | { id: "RSF_WMS_TASK", name: "RSF_WMS_TASK" }, |
| | | { id: "RSF_WMS_BASE", name: "RSF_WMS_BASE" }, |
| | | ]} |
| | | fullWidth |
| | | disabled={readOnly} |
| | |
| | | {formData.transportType === "SSE_HTTP" && ( |
| | | <> |
| | | <Grid item xs={12}> |
| | | <TextInput source="serverUrl" label="服务地址" fullWidth disabled={readOnly} /> |
| | | <TextInput source="serverUrl" label="ai.mcp.fields.serverUrl" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="endpoint" label="SSE Endpoint" fullWidth disabled={readOnly} /> |
| | | <TextInput source="endpoint" label="ai.mcp.fields.endpoint" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="headersJson" label="Headers JSON" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | <TextInput source="headersJson" label="ai.mcp.fields.headersJson" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | </Grid> |
| | | </> |
| | | )} |
| | | {formData.transportType === "STDIO" && ( |
| | | <> |
| | | <Grid item xs={12}> |
| | | <TextInput source="command" label="命令" fullWidth disabled={readOnly} /> |
| | | <TextInput source="command" label="ai.mcp.fields.command" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="argsJson" label="Args JSON" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | <TextInput source="argsJson" label="ai.mcp.fields.argsJson" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="envJson" label="Env JSON" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | <TextInput source="envJson" label="ai.mcp.fields.envJson" fullWidth multiline minRows={4} disabled={readOnly} /> |
| | | </Grid> |
| | | </> |
| | | )} |
| | |
| | | )} |
| | | </FormDataConsumer> |
| | | <Grid item xs={12} md={4}> |
| | | <NumberInput source="requestTimeoutMs" label="Timeout(ms)" fullWidth disabled={readOnly} /> |
| | | <NumberInput source="requestTimeoutMs" label="ai.mcp.fields.requestTimeoutMs" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <NumberInput source="sort" label="排序" fullWidth disabled={readOnly} /> |
| | | <NumberInput source="sort" label="ai.mcp.fields.sort" fullWidth disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <StatusSelectInput disabled={readOnly} /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="memo" label="备注" fullWidth multiline minRows={3} disabled={readOnly} /> |
| | | <TextInput source="memo" label="common.field.memo" fullWidth multiline minRows={3} disabled={readOnly} /> |
| | | </Grid> |
| | | <FormDataConsumer> |
| | | {({ formData }) => <AiMcpDraftTestSection formData={formData} readOnly={readOnly} />} |
| | | </FormDataConsumer> |
| | | <Grid item xs={12}> |
| | | <Typography variant="h6">{translate("ai.mcp.form.sections.runtime")}</Typography> |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <TextInput source="healthStatus" label="ai.mcp.fields.healthStatus" fullWidth disabled /> |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <TextInput source="lastInitElapsedMs" label="ai.mcp.fields.lastInitElapsedMs" fullWidth disabled /> |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <TextInput source="lastTestTime$" label="ai.mcp.fields.lastTestTime" fullWidth disabled /> |
| | | </Grid> |
| | | <Grid item xs={12}> |
| | | <TextInput source="lastTestMessage" label="ai.mcp.fields.lastTestMessage" fullWidth multiline minRows={3} disabled /> |
| | | </Grid> |
| | | <Grid item xs={12} md={6}> |
| | | <TextInput source="updateBy" label="ai.common.lastUpdatedBy" fullWidth disabled /> |
| | | </Grid> |
| | | <Grid item xs={12} md={6}> |
| | | <TextInput source="updateTime$" label="ai.common.lastUpdatedAt" fullWidth disabled /> |
| | | </Grid> |
| | | </Grid> |
| | | ); |
| | | ); |
| | | }; |
| | | |
| | | export default AiMcpMountForm; |