From cfb11bfebee5b4ec56c822f27fe339c315e497cc Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期二, 03 二月 2026 17:20:31 +0800
Subject: [PATCH] #路径流程页

---
 rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx             |   22 ++
 rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx         |   12 +
 rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeEdit.jsx |   21 +
 rsf-admin/src/i18n/zh.js                                               |   19 ++
 rsf-admin/src/page/basicInfo/basStation/ContainerTypesField.jsx        |   20 +-
 rsf-admin/src/i18n/en.js                                               |   19 ++
 rsf-admin/src/page/ResourceContent.js                                  |    3 
 rsf-admin/src/config/setting.js                                        |    2 
 rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeList.jsx |  159 ++++++++++-------
 rsf-admin/src/page/components/ChipArrayField.jsx                       |  210 +++++++++++++++++++++++
 10 files changed, 396 insertions(+), 91 deletions(-)

diff --git a/rsf-admin/src/config/setting.js b/rsf-admin/src/config/setting.js
index 749c98d..60520aa 100644
--- a/rsf-admin/src/config/setting.js
+++ b/rsf-admin/src/config/setting.js
@@ -25,7 +25,7 @@
 
 export const ABORT_SIGNAL = false;
 
-export const DEFAULT_PAGE_SIZE = 20;
+export const DEFAULT_PAGE_SIZE = 25;
 
 export const DEFAULT_START_PAGE = 1;
 
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index ce9f2cb..a5aa5f4 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -224,9 +224,28 @@
         taskPathTemplateNode: 'TaskPathTemplateNode',
         subsystemFlowTemplate: 'SubsystemFlowTemplate',
         flowStepTemplate: 'FlowStepTemplate',
+        taskPathTemplateMerge: 'TaskPathTemplateMerge',
     },
     table: {
         field: {
+            taskPathTemplateMerge: {
+                templateCode: "templateCode",
+                templateName: "templateName",
+                sourceType: "sourceType",
+                targetType: "targetType",
+                conditionExpression: "conditionExpression",
+                conditionDesc: "conditionDesc",
+                version: "version",
+                isCurrent: "isCurrent",
+                effectiveTime: "effectiveTime",
+                expireTime: "expireTime",
+                priority: "priority",
+                timeoutMinutes: "timeoutMinutes",
+                maxRetryTimes: "maxRetryTimes",
+                retryIntervalSeconds: "retryIntervalSeconds",
+                remark: "remark",
+                stepSize: "stepSize",
+            },
             flowStepTemplate: {
                 flowId: "flowId",
                 flowCode: "flowCode",
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 4088f47..a982073 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -240,9 +240,28 @@
         taskPathTemplateNode: '浠诲姟璺緞妯℃澘鑺傜偣',
         subsystemFlowTemplate: '瀛愮郴缁熸祦绋嬫ā鏉�',
         flowStepTemplate: '娴佺▼姝ラ妯℃澘',
+        taskPathTemplateMerge: '浠诲姟璺緞妯℃澘鍚堝苟',
     },
     table: {
         field: {
+            taskPathTemplateMerge: {
+                templateCode: "妯℃澘缂栫爜",
+                templateName: "妯℃澘鍚嶇О",
+                sourceType: "婧愮被鍨�",
+                targetType: "鐩爣绫诲瀷",
+                conditionExpression: "鏉′欢琛ㄨ揪寮�",
+                conditionDesc: "鏉′欢鎻忚堪",
+                version: "鐗堟湰",
+                isCurrent: "鏄惁褰撳墠",
+                effectiveTime: "鐢熸晥鏃堕棿",
+                expireTime: "杩囨湡鏃堕棿",
+                priority: "浼樺厛绾�",
+                timeoutMinutes: "瓒呮椂鏃堕棿",
+                maxRetryTimes: "鏈�澶ч噸璇曟鏁�",
+                retryIntervalSeconds: "閲嶈瘯闂撮殧",
+                remark: "澶囨敞",
+                stepSize: "姝ラ暱",
+            },
             flowStepTemplate: {
                 flowId: "娴佺▼ID",
                 flowCode: "娴佺▼缂栫爜",
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 4820cef..88b9a97 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -68,6 +68,7 @@
 import preparation from "./orders/preparation";
 import menuPda from './menuPda';
 import taskPathTemplate from './taskPathTemplate';
+import taskPathTemplateMerge from './taskPathTemplateMerge';
 
 const ResourceContent = (node) => {
   switch (node.component) {
@@ -199,6 +200,8 @@
       return menuPda;
     case 'taskPathTemplate':
       return taskPathTemplate;
+    case 'taskPathTemplateMerge':
+      return taskPathTemplateMerge;
     // case "locItem":
     //   return locItem;
     default:
diff --git a/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx b/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
index 02ecb3c..092025f 100644
--- a/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
+++ b/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
@@ -47,6 +47,7 @@
 import WarehouseAreaField from "./WarehouseAreaField";
 import CrossZoneAreaField from "./CrossZoneAreaField";
 import ContainerTypesField from "./ContainerTypesField";
+import ChipArrayField from '@/page/components/ChipArrayField';
 
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
     '& .css-1vooibu-MuiSvgIcon-root': {
@@ -160,9 +161,18 @@
                         render={record => record.inAble === 1 ? '鏄�' : '鍚�'}
                     />
                     <WrapperField cellClassName="crossZoneArea" label="table.field.basStation.crossZoneArea">
-                        <CrossZoneAreaField
+                        {/* <CrossZoneAreaField
                             open={areaFieldDialog}
                             setOpen={setAreaFieldDialog}
+                        /> */}
+
+                        <ChipArrayField
+                            source="areaIds"
+                            apiEndpoint="/warehouseAreas/many/{ids}"
+                            labelField="name"
+                            dialogTitle={translate('table.field.basStation.crossZoneArea')}
+                            initialDisplayCount={1}
+                            placeholderText="{count} 涓尯鍩�"
                         />
                     </WrapperField>
                     <FunctionField
@@ -171,9 +181,13 @@
                         render={record => record.inAble === 1 ? '鏄�' : '鍚�'}
                     />
                     <WrapperField cellClassName="containerType" label="table.field.basStation.containerType">
-                        <ContainerTypesField
-                            open={areaFieldDialog2}
-                            setOpen={setAreaFieldDialog2}
+                        <ChipArrayField
+                            source="containerTypes$"
+                            apiEndpoint="/dictData/many/{ids}"
+                            labelField="label"
+                            dialogTitle={translate('table.field.basStation.containerType')}
+                            initialDisplayCount={1}
+                            placeholderText="{count} 涓尯鍩�"
                         />
                     </WrapperField>
                     <FunctionField
diff --git a/rsf-admin/src/page/basicInfo/basStation/ContainerTypesField.jsx b/rsf-admin/src/page/basicInfo/basStation/ContainerTypesField.jsx
index 0a69c5a..7495bf0 100644
--- a/rsf-admin/src/page/basicInfo/basStation/ContainerTypesField.jsx
+++ b/rsf-admin/src/page/basicInfo/basStation/ContainerTypesField.jsx
@@ -21,9 +21,9 @@
 
     const fetchAreaNames = async () => {
         if (!record?.containerTypes || record.containerTypes.length === 0) return;
-        
+
         setLoading(true);
-        try {            
+        try {
             const res = await request.post(`/dictData/many/${record.containerTypes$.join(',')}`);
             if (res?.data?.code === 200) {
                 setAreaNames(res.data.data || []);
@@ -36,10 +36,10 @@
     };
 
     React.useEffect(() => {
-        if (record?.containerTypes   && record.containerTypes.length > 0) {
+        if (record?.containerTypes && record.containerTypes.length > 0) {
             fetchAreaNames();
         }
-    }, [record]);    
+    }, [record]);
 
     if (loading) {
         return <CircularProgress size={20} />;
@@ -47,10 +47,10 @@
 
     return (
         <>
-            <Stack 
-                direction="row" 
-                gap={1} 
-                flexWrap="wrap" 
+            <Stack
+                direction="row"
+                gap={1}
+                flexWrap="wrap"
                 onClick={handleOpen}
                 sx={{ cursor: 'pointer' }}
             >
@@ -75,8 +75,8 @@
                 )}
             </Stack>
 
-            <Dialog 
-                open={open} 
+            <Dialog
+                open={open}
                 onClose={handleClose}
                 maxWidth="md"
                 fullWidth
diff --git a/rsf-admin/src/page/components/ChipArrayField.jsx b/rsf-admin/src/page/components/ChipArrayField.jsx
new file mode 100644
index 0000000..05a6123
--- /dev/null
+++ b/rsf-admin/src/page/components/ChipArrayField.jsx
@@ -0,0 +1,210 @@
+import * as React from 'react';
+import {
+    Stack,
+    Chip,
+    Dialog,
+    DialogTitle,
+    DialogContent,
+    IconButton,
+    CircularProgress
+} from '@mui/material';
+import { useRecordContext } from 'react-admin';
+import CloseIcon from '@mui/icons-material/Close';
+import request from '@/utils/request';
+
+/**
+ * 閫氱敤鑺墖鏁扮粍瀛楁缁勪欢
+ * 鐢ㄤ簬灞曠ず浠� API 鑾峰彇鐨勬暟缁勬暟鎹�,浠ヨ姱鐗囧舰寮忔樉绀�
+ * 
+ * @param {Object} props
+ * @param {string|string[]} props.source - 鏁版嵁婧愬瓧娈靛悕,鏀寔澶氫釜瀛楁鍚�(濡� ['areaIds', 'areas'])
+ * @param {string} props.apiEndpoint - API 绔偣妯℃澘,浣跨敤 {ids} 浣滀负鍗犱綅绗�,渚嬪: '/warehouseAreas/many/{ids}'
+ * @param {string} props.labelField - 鐢ㄤ簬鏄剧ず鐨勫瓧娈靛悕,榛樿涓� 'label'
+ * @param {string} props.dialogTitle - 寮圭獥鏍囬
+ * @param {number} props.initialDisplayCount - 鍒濆鏄剧ず鐨勬暟閲�,榛樿涓� 1
+ * @param {string} props.placeholderText - 鍗犱綅绗︽枃鏈ā鏉�,浣跨敤 {count} 浣滀负鍗犱綅绗�,榛樿涓� '{count} 椤�'
+ * @param {function} props.dataTransform - 鍙�夌殑鏁版嵁杞崲鍑芥暟,鐢ㄤ簬澶勭悊鐗规畩鏍煎紡鐨勬暟鎹簮
+ */
+const ChipArrayField = ({
+    source,
+    apiEndpoint,
+    labelField = 'label',
+    dialogTitle = '璇︽儏',
+    initialDisplayCount = 1,
+    placeholderText = '{count} 椤�',
+    dataTransform = null
+}) => {
+    const record = useRecordContext();
+    const [open, setOpen] = React.useState(false);
+    const [items, setItems] = React.useState([]);
+    const [loading, setLoading] = React.useState(false);
+
+    const handleOpen = () => {
+        setOpen(true);
+    };
+
+    const handleClose = () => {
+        setOpen(false);
+    };
+
+    // 浣跨敤 useMemo 缂撳瓨鏁版嵁婧愬��
+    const sourceData = React.useMemo(() => {
+        if (!record) return null;
+
+        // 鏀寔澶氫釜瀛楁鍚�
+        if (Array.isArray(source)) {
+            for (const field of source) {
+                if (record[field]) {
+                    return record[field];
+                }
+            }
+            return null;
+        }
+
+        return record[source];
+    }, [record, source]);
+
+    // 浣跨敤 useCallback 缂撳瓨 fetchItems 鍑芥暟
+    const fetchItems = React.useCallback(async () => {
+        let data = sourceData;
+        if (!data || data.length === 0) {
+            setItems([]);
+            return;
+        }
+
+        // 搴旂敤鑷畾涔夋暟鎹浆鎹㈠嚱鏁�
+        if (dataTransform) {
+            data = dataTransform(data);
+        }
+
+        setLoading(true);
+        try {
+            // 鎻愬彇 ID 鍜屾帓搴忎俊鎭�
+            const isObjectArray = data.length > 0 &&
+                typeof data[0] === 'object' &&
+                data[0] !== null &&
+                'id' in data[0];
+
+            let ids = [];
+            let sortMap = new Map(); // 瀛樺偍 id -> sort 鐨勬槧灏�
+
+            if (isObjectArray) {
+                // 瀵硅薄鏁扮粍鏍煎紡,鎻愬彇 ID 鍜屾帓搴忎俊鎭�
+                ids = data.map(item => {
+                    const id = item.id;
+                    sortMap.set(id, item.sort || 0);
+                    return id;
+                });
+            } else {
+                // 绾� ID 鏁扮粍鏍煎紡
+                ids = data.map(id => Number(id));
+            }
+
+            // 鏇挎崲 API 绔偣涓殑鍗犱綅绗�
+            const url = apiEndpoint.replace('{ids}', ids.join(','));
+            const res = await request.post(url);
+
+            if (res?.data?.code === 200) {
+                let fetchedItems = res.data.data || [];
+
+                // 濡傛灉鏈夋帓搴忎俊鎭�,鎸夋帓搴忓�兼帓搴�
+                if (sortMap.size > 0) {
+                    fetchedItems = fetchedItems.sort((a, b) => {
+                        const sortA = sortMap.get(a.id) || 0;
+                        const sortB = sortMap.get(b.id) || 0;
+                        return sortA - sortB;
+                    });
+                }
+
+                setItems(fetchedItems);
+            }
+        } catch (error) {
+            console.error('鑾峰彇鏁版嵁澶辫触:', error);
+        } finally {
+            setLoading(false);
+        }
+    }, [sourceData, apiEndpoint, dataTransform]);
+
+    React.useEffect(() => {
+        if (sourceData && sourceData.length > 0) {
+            fetchItems();
+        } else {
+            setItems([]);
+        }
+    }, [sourceData, fetchItems]);
+
+    if (loading) {
+        return <CircularProgress size={20} />;
+    }
+
+    return (
+        <>
+            <Stack
+                direction="row"
+                gap={1}
+                flexWrap="wrap"
+                onClick={handleOpen}
+                sx={{ cursor: 'pointer' }}
+            >
+                {items.slice(0, initialDisplayCount).map((item) => (
+                    <Chip
+                        size="small"
+                        key={item.id}
+                        label={item[labelField] || item.id}
+                    />
+                ))}
+                {items.length > initialDisplayCount && (
+                    <Chip
+                        size="small"
+                        label={`+${items.length - initialDisplayCount}`}
+                    />
+                )}
+                {items.length === 0 && sourceData && sourceData.length > 0 && (
+                    <Chip
+                        size="small"
+                        label={placeholderText.replace('{count}', sourceData.length)}
+                    />
+                )}
+            </Stack>
+
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                maxWidth="md"
+                fullWidth
+            >
+                <DialogTitle>
+                    {dialogTitle}
+                    <IconButton
+                        aria-label="close"
+                        onClick={handleClose}
+                        sx={{
+                            position: 'absolute',
+                            right: 8,
+                            top: 8,
+                        }}
+                    >
+                        <CloseIcon />
+                    </IconButton>
+                </DialogTitle>
+                <DialogContent>
+                    {loading ? (
+                        <CircularProgress />
+                    ) : (
+                        <Stack direction="row" gap={1} flexWrap="wrap" sx={{ mt: 1 }}>
+                            {items.map((item) => (
+                                <Chip
+                                    size="small"
+                                    key={item.id}
+                                    label={item[labelField] || item.id}
+                                />
+                            ))}
+                        </Stack>
+                    )}
+                </DialogContent>
+            </Dialog>
+        </>
+    );
+};
+
+export default ChipArrayField;
diff --git a/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx b/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx
index c388420..959a8fe 100644
--- a/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx
+++ b/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx
@@ -12,15 +12,21 @@
 import StickyDataTable from "@/page/components/StickyDataTable";
 
 const ViewTable = ({ title, resource, filter, onClick, selectedId, columns }) => {
+    // 鍙湁鍦ㄦ湁 filter 鏃舵墠璋冪敤 useListController锛岄伩鍏嶄笉蹇呰鐨勮姹�
+    const shouldLoadData = !!filter;
+
     const listContext = useListController({
         resource: resource,
-        filter: filter,
+        filter: filter || {},
         perPage: 100,
         sort: { field: 'id', order: 'ASC' },
         disableSyncWithLocation: true,
+        queryOptions: {
+            enabled: shouldLoadData, // 鍙湁鍦� shouldLoadData 涓� true 鏃舵墠鎵ц鏌ヨ
+        },
     });
 
-    if (!filter) {
+    if (!shouldLoadData) {
         return (
             <Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
                 <Box p={1} borderBottom={1} borderColor="divider">
@@ -147,7 +153,7 @@
                     <ViewTable
                         title={translate('menu.subsystemFlowTemplate')}
                         resource="subsystemFlowTemplate"
-                        filter={selectedNode ? { systemCode: selectedNode.systemCode } : null}
+                        filter={selectedNode ? { flowCode: selectedNode.nodeCode } : null}
                         onClick={handleFlowClick}
                         selectedId={selectedFlow?.id}
                         columns={flowColumns}
diff --git a/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeEdit.jsx b/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeEdit.jsx
index 4a3d443..e197cb1 100644
--- a/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeEdit.jsx
+++ b/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeEdit.jsx
@@ -19,6 +19,7 @@
     required,
     useRecordContext,
     DeleteButton,
+    SelectArrayInput,
 } from 'react-admin';
 import { useWatch, useFormContext } from "react-hook-form";
 import { Stack, Grid, Box, Typography } from '@mui/material';
@@ -97,12 +98,20 @@
                             />
                         </Stack>
                         <Stack direction='row' gap={2}>
-                            <TextInput
-                                label="table.field.taskPathTemplateMerge.conditionExpression"
-                                source="conditionExpression"
-                                parse={v => v}
-                                validate={required()}
-                            />
+                            <ReferenceArrayInput source="conditionExpression" reference="taskPathTemplate">
+                                <SelectArrayInput
+                                    label="table.field.taskPathTemplateMerge.conditionExpression"
+                                    optionText="templateName"
+                                    optionValue="id"
+                                    fullWidth
+                                    validate={(value) => {
+                                        if (value && value.length > 1) {
+                                            return '鍙兘閫夋嫨涓�涓ā鏉�';
+                                        }
+                                        return undefined;
+                                    }}
+                                />
+                            </ReferenceArrayInput>
                         </Stack>
                         <Stack direction='row' gap={2}>
                             <TextInput
diff --git a/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeList.jsx b/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeList.jsx
index 859e34f..2f25037 100644
--- a/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeList.jsx
+++ b/rsf-admin/src/page/taskPathTemplateMerge/TaskPathTemplateMergeList.jsx
@@ -2,35 +2,25 @@
 import { useNavigate } from 'react-router-dom';
 import {
     List,
-    DatagridConfigurable,
     SearchInput,
     TopToolbar,
-    SelectColumnsButton,
+    ColumnsButton,
     EditButton,
     FilterButton,
-    CreateButton,
-    ExportButton,
-    BulkDeleteButton,
     WrapperField,
-    useRecordContext,
     useTranslate,
-    useNotify,
     useListContext,
-    FunctionField,
     TextField,
     NumberField,
     DateField,
     BooleanField,
     ReferenceField,
     TextInput,
-    DateTimeInput,
     DateInput,
     SelectInput,
     NumberInput,
-    ReferenceInput,
-    ReferenceArrayInput,
-    AutocompleteInput,
     DeleteButton,
+    DataTable
 } from 'react-admin';
 import { Box, Typography, Card, Stack } from '@mui/material';
 import { styled } from '@mui/material/styles';
@@ -43,20 +33,10 @@
 import MyField from "../components/MyField";
 import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
 import * as Common from '@/utils/common';
+import StickyDataTable from "@/page/components/StickyDataTable";
+import useTableLayout from '@/utils/useTableLayout';
+import ChipArrayField from '@/page/components/ChipArrayField';
 
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-    '& .css-1vooibu-MuiSvgIcon-root': {
-        height: '.9em'
-    },
-    '& .RaDatagrid-row': {
-        cursor: 'auto'
-    },
-    '& .column-name': {
-    },
-    '& .opt': {
-        width: 200
-    },
-}));
 
 const filters = [
     <SearchInput source="condition" alwaysOn />,
@@ -92,6 +72,49 @@
     />,
 ]
 
+const getColumns = (translate) => [
+    <NumberField source="id" />,
+    <TextField source="templateCode" label="table.field.taskPathTemplateMerge.templateCode" />,
+    <TextField source="templateName" label="table.field.taskPathTemplateMerge.templateName" />,
+    <TextField source="sourceType" label="table.field.taskPathTemplateMerge.sourceType" />,
+    <TextField source="targetType" label="table.field.taskPathTemplateMerge.targetType" />,
+    <ChipArrayField
+        source="conditionExpression"
+        label="table.field.taskPathTemplateMerge.conditionExpression"
+        apiEndpoint="/taskPathTemplate/many/{ids}"
+        labelField="templateName"
+        dialogTitle={translate('table.field.taskPathTemplateMerge.conditionExpression')}
+        initialDisplayCount={1}
+        placeholderText="{count} 涓ā鏉�"
+    />,
+    <TextField source="conditionDesc" label="table.field.taskPathTemplateMerge.conditionDesc" />,
+    <NumberField source="version" label="table.field.taskPathTemplateMerge.version" />,
+    <NumberField source="isCurrent" label="table.field.taskPathTemplateMerge.isCurrent" />,
+    <DateField source="effectiveTime" label="table.field.taskPathTemplateMerge.effectiveTime" showTime />,
+    <DateField source="expireTime" label="table.field.taskPathTemplateMerge.expireTime" showTime />,
+    <NumberField source="priority" label="table.field.taskPathTemplateMerge.priority" />,
+    <NumberField source="timeoutMinutes" label="table.field.taskPathTemplateMerge.timeoutMinutes" />,
+    <NumberField source="maxRetryTimes" label="table.field.taskPathTemplateMerge.maxRetryTimes" />,
+    <NumberField source="retryIntervalSeconds" label="table.field.taskPathTemplateMerge.retryIntervalSeconds" />,
+    <TextField source="remark" label="table.field.taskPathTemplateMerge.remark" />,
+    <NumberField source="stepSize" label="table.field.taskPathTemplateMerge.stepSize" />,
+
+    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
+        <TextField source="nickname" />
+    </ReferenceField>,
+    <DateField source="updateTime" label="common.field.updateTime" showTime />,
+    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
+        <TextField source="nickname" />
+    </ReferenceField>,
+    <DateField source="createTime" label="common.field.createTime" showTime />,
+    <BooleanField source="statusBool" label="common.field.status" sortable={false} />,
+    <TextField source="memo" label="common.field.memo" sortable={false} />,
+    <WrapperField source="opt" cellClassName="opt" label="common.field.opt">
+        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
+        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
+    </WrapperField>
+];
+
 const TaskPathTemplateMergeList = () => {
     const translate = useTranslate();
 
@@ -117,53 +140,13 @@
                     <TopToolbar>
                         <FilterButton />
                         <MyCreateButton onClick={() => { setCreateDialog(true) }} />
-                        <SelectColumnsButton preferenceKey='taskPathTemplateMerge' />
+                        <ColumnsButton storeKey='taskPathTemplateMerge' />
                         <MyExportButton />
                     </TopToolbar>
                 )}
                 perPage={DEFAULT_PAGE_SIZE}
             >
-                <StyledDatagrid
-                    preferenceKey='taskPathTemplateMerge'
-                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
-                    rowClick={(id, resource, record) => false}
-                    expand={() => <TaskPathTemplateMergePanel />}
-                    expandSingle={true}
-                    omit={['id', 'createTime', 'createBy', 'memo']}
-                >
-                    <NumberField source="id" />
-                    <TextField source="templateCode" label="table.field.taskPathTemplateMerge.templateCode" />
-                    <TextField source="templateName" label="table.field.taskPathTemplateMerge.templateName" />
-                    <TextField source="sourceType" label="table.field.taskPathTemplateMerge.sourceType" />
-                    <TextField source="targetType" label="table.field.taskPathTemplateMerge.targetType" />
-                    <TextField source="conditionExpression" label="table.field.taskPathTemplateMerge.conditionExpression" />
-                    <TextField source="conditionDesc" label="table.field.taskPathTemplateMerge.conditionDesc" />
-                    <NumberField source="version" label="table.field.taskPathTemplateMerge.version" />
-                    <NumberField source="isCurrent" label="table.field.taskPathTemplateMerge.isCurrent" />
-                    <DateField source="effectiveTime" label="table.field.taskPathTemplateMerge.effectiveTime" showTime />
-                    <DateField source="expireTime" label="table.field.taskPathTemplateMerge.expireTime" showTime />
-                    <NumberField source="priority" label="table.field.taskPathTemplateMerge.priority" />
-                    <NumberField source="timeoutMinutes" label="table.field.taskPathTemplateMerge.timeoutMinutes" />
-                    <NumberField source="maxRetryTimes" label="table.field.taskPathTemplateMerge.maxRetryTimes" />
-                    <NumberField source="retryIntervalSeconds" label="table.field.taskPathTemplateMerge.retryIntervalSeconds" />
-                    <TextField source="remark" label="table.field.taskPathTemplateMerge.remark" />
-                    <NumberField source="stepSize" label="table.field.taskPathTemplateMerge.stepSize" />
-
-                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
-                        <TextField source="nickname" />
-                    </ReferenceField>
-                    <DateField source="updateTime" label="common.field.updateTime" showTime />
-                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
-                        <TextField source="nickname" />
-                    </ReferenceField>
-                    <DateField source="createTime" label="common.field.createTime" showTime />
-                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
-                    <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>
+                <TableItems drawerVal={drawerVal} />
             </List>
             <TaskPathTemplateMergeCreate
                 open={createDialog}
@@ -179,4 +162,46 @@
     )
 }
 
+const TableItems = ({ drawerVal }) => {
+    const { isLoading } = useListContext();
+    const translate = useTranslate();
+    const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
+    const columns = getColumns(translate);
+
+    return (
+        <Box sx={{
+            position: 'relative',
+            maxHeight: boxMaxHeight,
+            maxWidth: boxMaxWidth,
+            overflowX: 'auto',
+            overflowY: 'auto',
+            '& .MuiTableCell-root': {
+                whiteSpace: 'nowrap',
+            }
+        }}>
+            {columns.length > 0 &&
+                <StickyDataTable
+                    stickyRight={['opt']}
+                    storeKey='taskPathTemplateMerge'
+                    bulkActionButtons={false}
+                    rowClick={false}
+                    hiddenColumns={['id', 'createTime', 'createBy', 'memo', 'statusBool']}
+                >
+                    {columns
+                        .map((column) => (
+                            <DataTable.Col
+                                key={column.key || column.props.source}
+                                source={column.props.source}
+                                label={column.props.label}
+                                sx={column.props.sx}
+                            >
+                                {column}
+                            </DataTable.Col>
+                        ))
+                    }
+                </StickyDataTable>}
+        </Box>
+    )
+}
+
 export default TaskPathTemplateMergeList;

--
Gitblit v1.9.1