From 00a44859a673b388e1dca5f54c4ecaffc5fee30e Mon Sep 17 00:00:00 2001
From: 1 <1@123>
Date: 星期四, 19 三月 2026 10:21:08 +0800
Subject: [PATCH] lsh#0:新增、更新、解禁(只要调用了更新,就代表非禁用了) 1:禁用
---
rsf-admin/src/page/system/aiParam/AiParamList.jsx | 326 ++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 232 insertions(+), 94 deletions(-)
diff --git a/rsf-admin/src/page/system/aiParam/AiParamList.jsx b/rsf-admin/src/page/system/aiParam/AiParamList.jsx
index 8f12399..f7d167f 100644
--- a/rsf-admin/src/page/system/aiParam/AiParamList.jsx
+++ b/rsf-admin/src/page/system/aiParam/AiParamList.jsx
@@ -1,120 +1,258 @@
-import React, { useState } from "react";
+import React, { useMemo, useState } from "react";
import {
- List,
- DatagridConfigurable,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
FilterButton,
- BulkDeleteButton,
- WrapperField,
- TextField,
- NumberField,
- DateField,
- BooleanField,
- TextInput,
- DateInput,
+ List,
+ SearchInput,
SelectInput,
- DeleteButton,
-} from 'react-admin';
-import { Box } from '@mui/material';
-import { styled } from '@mui/material/styles';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import AiParamCreate from "./AiParamCreate";
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
- '& .css-1vooibu-MuiSvgIcon-root': {
- height: '.9em'
- },
- '& .RaDatagrid-row': {
- cursor: 'auto'
- },
- '& .opt': {
- width: 200
- },
-}));
+ TextInput,
+ TopToolbar,
+ useDelete,
+ useListContext,
+ useNotify,
+ useRefresh,
+} from "react-admin";
+import {
+ Box,
+ Button,
+ Card,
+ CardActions,
+ CardContent,
+ Chip,
+ CircularProgress,
+ Divider,
+ Grid,
+ Stack,
+ Typography,
+} from "@mui/material";
+import AddRoundedIcon from "@mui/icons-material/AddRounded";
+import DeleteOutlineOutlinedIcon from "@mui/icons-material/DeleteOutlineOutlined";
+import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
+import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
+import MyExportButton from "@/page/components/MyExportButton";
+import AiParamForm from "./AiParamForm";
+import AiConfigDialog from "../aiShared/AiConfigDialog";
const filters = [
<SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <TextInput source="name" label="table.field.aiParam.name" />,
- <TextInput source="modelCode" label="table.field.aiParam.modelCode" />,
- <TextInput source="provider" label="table.field.aiParam.provider" />,
- <TextInput source="modelName" label="table.field.aiParam.modelName" />,
+ <TextInput source="providerType" label="鎻愪緵鏂圭被鍨�" />,
+ <TextInput source="model" label="妯″瀷" />,
<SelectInput
- source="defaultFlag"
- label="table.field.aiParam.defaultFlag"
- choices={[
- { id: '1', name: 'common.enums.true' },
- { id: '0', name: 'common.enums.false' },
- ]}
- />,
- <TextInput label="common.field.memo" source="memo" />,
- <SelectInput
- label="common.field.status"
source="status"
+ label="鐘舵��"
choices={[
- { id: '1', name: 'common.enums.statusTrue' },
- { id: '0', name: 'common.enums.statusFalse' },
+ { id: "1", name: "common.enums.statusTrue" },
+ { id: "0", name: "common.enums.statusFalse" },
]}
/>,
-]
+];
-const AiParamList = () => {
- const [createDialog, setCreateDialog] = useState(false);
+const defaultValues = {
+ providerType: "OPENAI_COMPATIBLE",
+ temperature: 0.7,
+ topP: 1,
+ timeoutMs: 60000,
+ streamingEnabled: true,
+ status: 1,
+};
+
+const truncateText = (value, max = 84) => {
+ if (!value) {
+ return "--";
+ }
+ return value.length > max ? `${value.slice(0, max)}...` : value;
+};
+
+const AiParamCards = ({ onView, onEdit, onDelete, deleting }) => {
+ const { data, isLoading } = useListContext();
+ const records = useMemo(() => (Array.isArray(data) ? data : []), [data]);
+
+ if (isLoading) {
+ return (
+ <Box display="flex" justifyContent="center" py={8}>
+ <CircularProgress size={28} />
+ </Box>
+ );
+ }
+
+ if (!records.length) {
+ return (
+ <Box px={2} py={6}>
+ <Card variant="outlined" sx={{ p: 3, textAlign: "center", borderStyle: "dashed" }}>
+ <Typography variant="subtitle1">鏆傛棤 AI 鍙傛暟閰嶇疆</Typography>
+ <Typography variant="body2" color="text.secondary" mt={1}>
+ 鍙互鍏堟柊寤轰竴涓� OpenAI 鍏煎妯″瀷鍙傛暟鍗$墖銆�
+ </Typography>
+ </Card>
+ </Box>
+ );
+ }
return (
- <Box display="flex">
+ <Box px={2} py={2}>
+ <Grid container spacing={2}>
+ {records.map((record) => (
+ <Grid item xs={12} md={6} xl={4} key={record.id}>
+ <Card
+ variant="outlined"
+ sx={{
+ height: "100%",
+ borderRadius: 3,
+ boxShadow: "0 8px 24px rgba(15, 23, 42, 0.06)",
+ }}
+ >
+ <CardContent sx={{ pb: 1.5 }}>
+ <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
+ <Box>
+ <Typography variant="h6" sx={{ mb: 0.5 }}>
+ {record.name}
+ </Typography>
+ <Typography variant="body2" color="text.secondary">
+ {record.model || "--"}
+ </Typography>
+ </Box>
+ <Chip
+ size="small"
+ color={record.statusBool ? "success" : "default"}
+ label={record.statusBool ? "鍚敤" : "鍋滅敤"}
+ />
+ </Stack>
+ <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap mt={1.5}>
+ <Chip size="small" variant="outlined" label={record.providerType$ || "OPENAI_COMPATIBLE"} />
+ <Chip
+ size="small"
+ variant="outlined"
+ color={record.streamingEnabled ? "info" : "default"}
+ label={record.streamingEnabled ? "娴佸紡鍝嶅簲" : "闈炴祦寮�"}
+ />
+ </Stack>
+ <Divider sx={{ my: 1.5 }} />
+ <Typography variant="body2" color="text.secondary">
+ Base URL
+ </Typography>
+ <Typography variant="body2" sx={{ mb: 1.5, wordBreak: "break-all" }}>
+ {truncateText(record.baseUrl, 120)}
+ </Typography>
+ <Grid container spacing={1}>
+ <Grid item xs={6}>
+ <Typography variant="caption" color="text.secondary">Temperature</Typography>
+ <Typography variant="body2">{record.temperature ?? "--"}</Typography>
+ </Grid>
+ <Grid item xs={6}>
+ <Typography variant="caption" color="text.secondary">Top P</Typography>
+ <Typography variant="body2">{record.topP ?? "--"}</Typography>
+ </Grid>
+ <Grid item xs={6}>
+ <Typography variant="caption" color="text.secondary">Max Tokens</Typography>
+ <Typography variant="body2">{record.maxTokens ?? "--"}</Typography>
+ </Grid>
+ <Grid item xs={6}>
+ <Typography variant="caption" color="text.secondary">Timeout</Typography>
+ <Typography variant="body2">{record.timeoutMs ?? "--"} ms</Typography>
+ </Grid>
+ </Grid>
+ <Typography variant="caption" color="text.secondary" display="block" mt={1.5}>
+ 澶囨敞
+ </Typography>
+ <Typography variant="body2">{truncateText(record.memo)}</Typography>
+ </CardContent>
+ <CardActions sx={{ px: 2, pb: 2, pt: 0, justifyContent: "space-between" }}>
+ <Stack direction="row" spacing={1}>
+ <Button size="small" startIcon={<VisibilityOutlinedIcon />} onClick={() => onView(record.id)}>
+ 璇︽儏
+ </Button>
+ <Button size="small" startIcon={<EditOutlinedIcon />} onClick={() => onEdit(record.id)}>
+ 缂栬緫
+ </Button>
+ </Stack>
+ <Button
+ size="small"
+ color="error"
+ startIcon={<DeleteOutlineOutlinedIcon />}
+ onClick={() => onDelete(record)}
+ disabled={deleting}
+ >
+ 鍒犻櫎
+ </Button>
+ </CardActions>
+ </Card>
+ </Grid>
+ ))}
+ </Grid>
+ </Box>
+ );
+};
+
+const AiParamList = () => {
+ const notify = useNotify();
+ const refresh = useRefresh();
+ const [deleteOne, { isPending: deleting }] = useDelete();
+ const [dialogState, setDialogState] = useState({ open: false, mode: "create", recordId: null });
+
+ const openDialog = (mode, recordId = null) => setDialogState({ open: true, mode, recordId });
+ const closeDialog = () => setDialogState({ open: false, mode: "create", recordId: null });
+
+ const handleDelete = (record) => {
+ if (!record?.id || !window.confirm(`纭鍒犻櫎鈥�${record.name}鈥濆悧锛焋)) {
+ return;
+ }
+ deleteOne(
+ "aiParam",
+ { id: record.id },
+ {
+ onSuccess: () => {
+ notify("鍒犻櫎鎴愬姛");
+ refresh();
+ },
+ onError: (error) => {
+ notify(error?.message || "鍒犻櫎澶辫触", { type: "error" });
+ },
+ }
+ );
+ };
+
+ const dialogTitle = {
+ create: "鏂板缓 AI 鍙傛暟",
+ edit: "缂栬緫 AI 鍙傛暟",
+ show: "鏌ョ湅 AI 鍙傛暟璇︽儏",
+ }[dialogState.mode];
+
+ return (
+ <>
<List
- title={"menu.aiParam"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+ title="menu.aiParam"
filters={filters}
- sort={{ field: "sort", order: "asc" }}
+ sort={{ field: "create_time", order: "desc" }}
actions={(
<TopToolbar>
<FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiParam' />
+ <Button variant="contained" startIcon={<AddRoundedIcon />} onClick={() => openDialog("create")}>
+ 鏂板缓
+ </Button>
<MyExportButton />
</TopToolbar>
)}
- perPage={DEFAULT_PAGE_SIZE}
>
- <StyledDatagrid
- preferenceKey='aiParam'
- bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
- rowClick={false}
- omit={['id', 'createTime', 'memo', 'statusBool', 'defaultFlagBool']}
- >
- <NumberField source="id" />
- <TextField source="uuid" label="table.field.aiParam.uuid" />
- <TextField source="name" label="table.field.aiParam.name" />
- <TextField source="modelCode" label="table.field.aiParam.modelCode" />
- <TextField source="provider" label="table.field.aiParam.provider" />
- <TextField source="modelName" label="table.field.aiParam.modelName" />
- <NumberField source="maxContextMessages" label="table.field.aiParam.maxContextMessages" />
- <NumberField source="sort" label="table.field.aiParam.sort" />
- <BooleanField source="defaultFlagBool" label="table.field.aiParam.defaultFlag" sortable={false} />
- <BooleanField source="statusBool" label="common.field.status" sortable={false} />
- <DateField source="updateTime" label="common.field.updateTime" showTime />
- <TextField source="memo" label="common.field.memo" sortable={false} />
- <WrapperField cellClassName="opt" label="common.field.opt">
- <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
- <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
- </WrapperField>
- </StyledDatagrid>
+ <AiParamCards
+ onView={(id) => openDialog("show", id)}
+ onEdit={(id) => openDialog("edit", id)}
+ onDelete={handleDelete}
+ deleting={deleting}
+ />
</List>
- <AiParamCreate
- open={createDialog}
- setOpen={setCreateDialog}
- />
- </Box>
- )
-}
+ <AiConfigDialog
+ open={dialogState.open}
+ mode={dialogState.mode}
+ title={dialogTitle}
+ resource="aiParam"
+ recordId={dialogState.recordId}
+ defaultValues={defaultValues}
+ maxWidth="md"
+ onClose={closeDialog}
+ >
+ <AiParamForm readOnly={dialogState.mode === "show"} />
+ </AiConfigDialog>
+ </>
+ );
+};
export default AiParamList;
--
Gitblit v1.9.1