From 2dae7a77781f4ef123a673893a9a7ffb34285f8f Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期二, 03 二月 2026 14:34:13 +0800
Subject: [PATCH] #列浮动和路径流程页
---
rsf-admin/src/page/taskPathTemplate/TaskPathTemplateList.jsx | 203 ++++++++++++----
rsf-server/src/main/java/com/vincent/rsf/server/system/controller/FlowStepTemplateController.java | 9
rsf-admin/src/utils/useTableLayout.js | 33 ++
rsf-server/src/main/java/com/vincent/rsf/server/system/controller/TaskPathTemplateNodeController.java | 18
rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx | 169 ++++++++++++++
rsf-admin/src/i18n/zh.js | 76 ++++++
rsf-server/src/main/java/com/vincent/rsf/server/system/controller/SubsystemFlowTemplateController.java | 9
rsf-admin/src/i18n/en.js | 76 ++++++
rsf-admin/src/page/ResourceContent.js | 4
rsf-admin/src/page/orders/asnOrderItem/AsnOrderItemList.jsx | 27 +-
rsf-admin/src/page/components/StickyDataTable.jsx | 78 ++++++
11 files changed, 600 insertions(+), 102 deletions(-)
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index a12d985..ce9f2cb 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -220,9 +220,85 @@
statisticCount: 'Statistic Count',
preparation: "澶囨枡鍗�",
menuPda: 'MenuPda',
+ taskPathTemplate: 'TaskPathTemplate',
+ taskPathTemplateNode: 'TaskPathTemplateNode',
+ subsystemFlowTemplate: 'SubsystemFlowTemplate',
+ flowStepTemplate: 'FlowStepTemplate',
},
table: {
field: {
+ flowStepTemplate: {
+ flowId: "flowId",
+ flowCode: "flowCode",
+ stepOrder: "stepOrder",
+ stepCode: "stepCode",
+ stepName: "stepName",
+ stepType: "stepType",
+ actionType: "actionType",
+ actionConfig: "actionConfig",
+ inputMapping: "inputMapping",
+ outputMapping: "outputMapping",
+ conditionExpression: "conditionExpression",
+ skipOnFail: "skipOnFail",
+ retryEnabled: "retryEnabled",
+ retryConfig: "retryConfig",
+ timeoutSeconds: "timeoutSeconds",
+ },
+ subsystemFlowTemplate: {
+ flowCode: "flowCode",
+ flowName: "flowName",
+ systemCode: "systemCode",
+ systemName: "systemName",
+ nodeType: "nodeType",
+ version: "version",
+ isCurrent: "isCurrent",
+ effectiveTime: "effectiveTime",
+ timeoutStrategy: "timeoutStrategy",
+ timeoutSeconds: "timeoutSeconds",
+ maxRetryTimes: "maxRetryTimes",
+ needNotify: "needNotify",
+ notifyTemplate: "notifyTemplate",
+ remark: "remark",
+ },
+ taskPathTemplateNode: {
+ templateId: "templateId",
+ templateCode: "templateCode",
+ nodeOrder: "nodeOrder",
+ nodeCode: "nodeCode",
+ nodeName: "nodeName",
+ nodeType: "nodeType",
+ systemCode: "systemCode",
+ systemName: "systemName",
+ executeParams: "executeParams",
+ resultSchema: "resultSchema",
+ timeoutMinutes: "timeoutMinutes",
+ mandatory: "mandatory",
+ parallelExecutable: "parallelExecutable",
+ preCondition: "preCondition",
+ postCondition: "postCondition",
+ nextNodeRules: "nextNodeRules",
+ },
+ taskPathTemplate: {
+ 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",
+ createdBy: "createdBy",
+ updatedBy: "updatedBy",
+ createdTime: "createdTime",
+ updatedTime: "updatedTime",
+ },
outBound: {
stockWithdrawal: 'StockWithdrawal',
withdrawal: 'Withdrawal'
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 3189fc4..4088f47 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -236,9 +236,85 @@
freeze: '搴撳瓨鍐荤粨',
transferPoces: '璋冩嫧绠$悊',
menuPda: 'PDA鑿滃崟',
+ taskPathTemplate: '浠诲姟璺緞妯℃澘',
+ taskPathTemplateNode: '浠诲姟璺緞妯℃澘鑺傜偣',
+ subsystemFlowTemplate: '瀛愮郴缁熸祦绋嬫ā鏉�',
+ flowStepTemplate: '娴佺▼姝ラ妯℃澘',
},
table: {
field: {
+ flowStepTemplate: {
+ flowId: "娴佺▼ID",
+ flowCode: "娴佺▼缂栫爜",
+ stepOrder: "姝ラ椤哄簭",
+ stepCode: "姝ラ缂栫爜",
+ stepName: "姝ラ鍚嶇О",
+ stepType: "姝ラ绫诲瀷",
+ actionType: "鍔ㄤ綔绫诲瀷",
+ actionConfig: "鍔ㄤ綔閰嶇疆",
+ inputMapping: "杈撳叆鏄犲皠",
+ outputMapping: "杈撳嚭鏄犲皠",
+ conditionExpression: "鏉′欢琛ㄨ揪寮�",
+ skipOnFail: "澶辫触璺宠繃",
+ retryEnabled: "閲嶈瘯鍚敤",
+ retryConfig: "閲嶈瘯閰嶇疆",
+ timeoutSeconds: "瓒呮椂绉掓暟",
+ },
+ subsystemFlowTemplate: {
+ flowCode: "娴佺▼缂栫爜",
+ flowName: "娴佺▼鍚嶇О",
+ systemCode: "绯荤粺缂栫爜",
+ systemName: "绯荤粺鍚嶇О",
+ nodeType: "鑺傜偣绫诲瀷",
+ version: "鐗堟湰",
+ isCurrent: "鏄惁褰撳墠",
+ effectiveTime: "鐢熸晥鏃堕棿",
+ timeoutStrategy: "瓒呮椂绛栫暐",
+ timeoutSeconds: "瓒呮椂绉掓暟",
+ maxRetryTimes: "鏈�澶ч噸璇曟鏁�",
+ needNotify: "鏄惁閫氱煡",
+ notifyTemplate: "閫氱煡妯℃澘",
+ remark: "澶囨敞",
+ },
+ taskPathTemplateNode: {
+ templateId: "妯℃澘ID",
+ templateCode: "妯℃澘缂栫爜",
+ nodeOrder: "鑺傜偣椤哄簭",
+ nodeCode: "鑺傜偣缂栫爜",
+ nodeName: "鑺傜偣鍚嶇О",
+ nodeType: "鑺傜偣绫诲瀷",
+ systemCode: "绯荤粺缂栫爜",
+ systemName: "绯荤粺鍚嶇О",
+ executeParams: "鎵ц鍙傛暟",
+ resultSchema: "缁撴灉Schema",
+ timeoutMinutes: "瓒呮椂鏃堕棿",
+ mandatory: "鏄惁蹇呭~",
+ parallelExecutable: "鏄惁骞惰鎵ц",
+ preCondition: "鍓嶇疆鏉′欢",
+ postCondition: "鍚庣疆鏉′欢",
+ nextNodeRules: "鑺傜偣瑙勫垯",
+ },
+ taskPathTemplate: {
+ templateCode: "妯℃澘缂栫爜",
+ templateName: "妯℃澘鍚嶇О",
+ sourceType: "婧愮被鍨�",
+ targetType: "鐩爣绫诲瀷",
+ conditionExpression: "鏉′欢琛ㄨ揪寮�",
+ conditionDesc: "鏉′欢鎻忚堪",
+ version: "鐗堟湰",
+ isCurrent: "鏄惁褰撳墠",
+ effectiveTime: "鐢熸晥鏃堕棿",
+ expireTime: "杩囨湡鏃堕棿",
+ priority: "浼樺厛绾�",
+ timeoutMinutes: "瓒呮椂鏃堕棿",
+ maxRetryTimes: "鏈�澶ч噸璇曟鏁�",
+ retryIntervalSeconds: "閲嶈瘯闂撮殧",
+ remark: "澶囨敞",
+ createdBy: "鍒涘缓浜�",
+ updatedBy: "鏇存柊浜�",
+ createdTime: "鍒涘缓鏃堕棿",
+ updatedTime: "鏇存柊鏃堕棿",
+ },
stockTransfer: {
orgLoc: '婧愬簱浣�',
tarLoc: '鐩爣搴撲綅',
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index e7bcb63..4820cef 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -67,7 +67,7 @@
import statisticCount from './statistics/stockStatisticNum';
import preparation from "./orders/preparation";
import menuPda from './menuPda';
-// import locItem from "./basicInfo/locItem";
+import taskPathTemplate from './taskPathTemplate';
const ResourceContent = (node) => {
switch (node.component) {
@@ -197,6 +197,8 @@
return preparation;
case 'menuPda':
return menuPda;
+ case 'taskPathTemplate':
+ return taskPathTemplate;
// case "locItem":
// return locItem;
default:
diff --git a/rsf-admin/src/page/components/StickyDataTable.jsx b/rsf-admin/src/page/components/StickyDataTable.jsx
new file mode 100644
index 0000000..fe4189b
--- /dev/null
+++ b/rsf-admin/src/page/components/StickyDataTable.jsx
@@ -0,0 +1,78 @@
+
+import React from 'react';
+import { DataTable } from 'react-admin';
+
+/**
+ * StickyDataTable Component
+ *
+ * 灏佽 react-admin 鐨� DataTable锛屽疄鐜颁紶鍏ュ垪鍚嶅嵆鍙浐瀹氬垪銆�
+ *
+ * @param {Object} props
+ * @param {string[]} props.stickyLeft - 闇�瑕佸浐瀹氬湪宸︿晶鐨勫瓧娈� source 鍒楄〃
+ * @param {string[]} props.stickyRight - 闇�瑕佸浐瀹氬湪鍙充晶鐨勫瓧娈� source 鍒楄〃
+ */
+export const StickyDataTable = ({ stickyLeft = [], stickyRight = [], children, ...props }) => {
+
+ // 閫掑綊澶勭悊 Children锛岀‘淇濆嵆渚挎槸 Fragment 鍖呰9鐨勫垪涔熻兘琚鐞�
+ const processChildren = (children) => {
+ return React.Children.map(children, (child) => {
+ if (!React.isValidElement(child)) return child;
+
+ // 濡傛灉鏄� Fragment锛岄�掑綊澶勭悊鍏� children
+ if (child.type === React.Fragment) {
+ return <React.Fragment>{processChildren(child.props.children)}</React.Fragment>;
+ }
+
+ const source = child.props.source;
+ let stickyStyle = {};
+
+ // 宸︿晶鍥哄畾
+ if (stickyLeft.includes(source)) {
+ stickyStyle = {
+ position: 'sticky',
+ left: 0,
+ zIndex: 2, // 姣旀櫘閫氬唴瀹归珮
+ backgroundColor: '#FFFFFF',
+ '.MuiTableRow-root:not(.MuiTableRow-head):hover &': {
+ backgroundColor: '#f5f5f5'
+ }
+ };
+ }
+
+ // 鍙充晶鍥哄畾
+ if (stickyRight.includes(source)) {
+ stickyStyle = {
+ position: 'sticky',
+ right: 0,
+ zIndex: 2,
+ backgroundColor: '#FFFFFF',
+ '.MuiTableRow-root:not(.MuiTableRow-head):hover &': {
+ backgroundColor: '#f5f5f5'
+ }
+ };
+ }
+
+ if (Object.keys(stickyStyle).length > 0) {
+ // 鍚堝苟 sx
+ return React.cloneElement(child, {
+ sx: { ...child.props.sx, ...stickyStyle }
+ });
+ }
+
+ return child;
+ });
+ };
+
+ return (
+ <DataTable {...props} sx={{
+ '& .MuiTableCell-head': {
+ zIndex: 4,
+ borderBottom: 'none' // 閬靛惊涔嬪墠鐨勪紭鍖栵紝鍘婚櫎琛ㄥご涓嬭竟妗�
+ },
+ }}>
+ {processChildren(children)}
+ </DataTable>
+ );
+};
+
+export default StickyDataTable;
diff --git a/rsf-admin/src/page/orders/asnOrderItem/AsnOrderItemList.jsx b/rsf-admin/src/page/orders/asnOrderItem/AsnOrderItemList.jsx
index ff77191..a541c22 100644
--- a/rsf-admin/src/page/orders/asnOrderItem/AsnOrderItemList.jsx
+++ b/rsf-admin/src/page/orders/asnOrderItem/AsnOrderItemList.jsx
@@ -39,6 +39,8 @@
import { Box, Typography, Card, Stack, Dialog, DialogActions, DialogTitle, LinearProgress } from '@mui/material';
import { styled } from '@mui/material/styles';
import PageDrawer from "../../components/PageDrawer";
+import StickyDataTable from "../../components/StickyDataTable";
+import useTableLayout from '@/utils/useTableLayout';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import { fetchInOrderItemColumns } from '../config/orderItemColumns';
@@ -128,13 +130,11 @@
const [columns, setColumns] = useState([]);
const { isLoading } = useListContext();
const refresh = useRefresh();
- const [sidebarIsOpen] = useSidebarState();
-
const omittedFields = [
- 'id', 'orderId', 'orderCode', 'poCode', 'poId', 'wkType', 'type', 'checkType',
- 'spec', 'model', 'purQty', 'purUnit', 'qrcode', 'trackCode', 'splrCode',
- 'splrName', 'projectCode', 'supplierId', 'supplierName', 'priceUnitId',
- 'shipperId', 'businessTime', 'extendFields.[businessTime]',
+ 'id', 'orderId', 'orderCode', 'poCode', 'poId', 'wkType', 'type', 'checkType',
+ 'spec', 'model', 'purQty', 'purUnit', 'qrcode', 'trackCode', 'splrCode',
+ 'splrName', 'projectCode', 'supplierId', 'supplierName', 'priceUnitId',
+ 'shipperId', 'businessTime', 'extendFields.[businessTime]',
'extendFields.[wkType]', 'extendFields.[type]'
];
@@ -158,16 +158,11 @@
}
}
- const sidebarWidth = sidebarIsOpen ? 200 : 50;
- const contentPadding = 10; // 棰勭暀杈硅窛
- const rightDrawerWidth = drawerVal ? PAGE_DRAWER_WIDTH : 0;
- const boxMaxWidth = `calc(100vw - ${sidebarWidth + rightDrawerWidth + contentPadding}px)`;
- // 璁$畻 maxHeight: 100vh - (Header+Tabs 86px) - (Toolbar ~50px) - (Filters ~60px) - (Pagination ~50px) - (Padding ~40px) 鈮� 290px
- const boxMaxHeight = `calc(100vh - 210px)`;
+ const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
return (
<Box sx={{
- position: 'relative',
+ position: 'relative',
maxHeight: boxMaxHeight,
maxWidth: boxMaxWidth,
overflowX: 'auto',
@@ -188,7 +183,8 @@
/>
)}
{columns.length > 0 &&
- <DataTable
+ <StickyDataTable
+ stickyRight={['createTime']}
storeKey='asnOrderItem'
bulkActionButtons={false}
rowClick={false}
@@ -200,12 +196,13 @@
key={column.key || column.props.source}
source={column.props.source}
label={column.props.label}
+ sx={column.props.sx}
>
{column}
</DataTable.Col>
))
}
- </DataTable>}
+ </StickyDataTable>}
</Box>
)
}
diff --git a/rsf-admin/src/page/taskPathTemplate/TaskPathTemplateList.jsx b/rsf-admin/src/page/taskPathTemplate/TaskPathTemplateList.jsx
index 56117a7..7e3d17f 100644
--- a/rsf-admin/src/page/taskPathTemplate/TaskPathTemplateList.jsx
+++ b/rsf-admin/src/page/taskPathTemplate/TaskPathTemplateList.jsx
@@ -2,10 +2,11 @@
import { useNavigate } from 'react-router-dom';
import {
List,
+ DataTable,
DatagridConfigurable,
SearchInput,
TopToolbar,
- SelectColumnsButton,
+ ColumnsButton,
EditButton,
FilterButton,
CreateButton,
@@ -43,20 +44,11 @@
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 { Dialog, DialogContent, DialogActions, Button } from '@mui/material';
+import TaskTemplateFlowViewer from "./TaskTemplateFlowViewer";
-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 />,
@@ -95,11 +87,54 @@
/>,
]
+const baseColumns = [
+ <NumberField source="id" />,
+ <TextField source="templateCode" label="table.field.taskPathTemplate.templateCode" />,
+ <TextField source="templateName" label="table.field.taskPathTemplate.templateName" />,
+ <TextField source="sourceType" label="table.field.taskPathTemplate.sourceType" />,
+ <TextField source="targetType" label="table.field.taskPathTemplate.targetType" />,
+ <TextField source="conditionExpression" label="table.field.taskPathTemplate.conditionExpression" />,
+ <TextField source="conditionDesc" label="table.field.taskPathTemplate.conditionDesc" />,
+ <NumberField source="version" label="table.field.taskPathTemplate.version" />,
+ <NumberField source="isCurrent" label="table.field.taskPathTemplate.isCurrent" />,
+ <DateField source="effectiveTime" label="table.field.taskPathTemplate.effectiveTime" showTime />,
+ <DateField source="expireTime" label="table.field.taskPathTemplate.expireTime" showTime />,
+ <NumberField source="priority" label="table.field.taskPathTemplate.priority" />,
+ <NumberField source="timeoutMinutes" label="table.field.taskPathTemplate.timeoutMinutes" />,
+ <NumberField source="maxRetryTimes" label="table.field.taskPathTemplate.maxRetryTimes" />,
+ <NumberField source="retryIntervalSeconds" label="table.field.taskPathTemplate.retryIntervalSeconds" />,
+ <TextField source="remark" label="table.field.taskPathTemplate.remark" />,
+ <TextField source="createdBy" label="table.field.taskPathTemplate.createdBy" />,
+ <TextField source="updatedBy" label="table.field.taskPathTemplate.updatedBy" />,
+ <DateField source="createdTime" label="table.field.taskPathTemplate.createdTime" showTime />,
+ <DateField source="updatedTime" label="table.field.taskPathTemplate.updatedTime" showTime />,
+
+ <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} />,
+]
+
const TaskPathTemplateList = () => {
const translate = useTranslate();
const [createDialog, setCreateDialog] = useState(false);
const [drawerVal, setDrawerVal] = useState(false);
+ const [flowDialog, setFlowDialog] = useState(false);
+ const [currentRecord, setCurrentRecord] = useState(null);
+
+ const handleOpenFlow = (record) => {
+ setCurrentRecord(record);
+ setFlowDialog(true);
+ };
+
+ const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
return (
<Box display="flex">
@@ -120,61 +155,33 @@
<TopToolbar>
<FilterButton />
<MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='taskPathTemplate' />
+ <ColumnsButton storeKey='taskPathTemplate' />
<MyExportButton />
</TopToolbar>
)}
perPage={DEFAULT_PAGE_SIZE}
>
- <StyledDatagrid
- preferenceKey='taskPathTemplate'
- bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
- rowClick={(id, resource, record) => false}
- expand={() => <TaskPathTemplatePanel />}
- expandSingle={true}
- omit={['id', 'createTime', 'createBy', 'memo']}
- >
- <NumberField source="id" />
- <TextField source="templateCode" label="table.field.taskPathTemplate.templateCode" />
- <TextField source="templateName" label="table.field.taskPathTemplate.templateName" />
- <TextField source="sourceType" label="table.field.taskPathTemplate.sourceType" />
- <TextField source="targetType" label="table.field.taskPathTemplate.targetType" />
- <TextField source="conditionExpression" label="table.field.taskPathTemplate.conditionExpression" />
- <TextField source="conditionDesc" label="table.field.taskPathTemplate.conditionDesc" />
- <NumberField source="version" label="table.field.taskPathTemplate.version" />
- <NumberField source="isCurrent" label="table.field.taskPathTemplate.isCurrent" />
- <DateField source="effectiveTime" label="table.field.taskPathTemplate.effectiveTime" showTime />
- <DateField source="expireTime" label="table.field.taskPathTemplate.expireTime" showTime />
- <NumberField source="priority" label="table.field.taskPathTemplate.priority" />
- <NumberField source="timeoutMinutes" label="table.field.taskPathTemplate.timeoutMinutes" />
- <NumberField source="maxRetryTimes" label="table.field.taskPathTemplate.maxRetryTimes" />
- <NumberField source="retryIntervalSeconds" label="table.field.taskPathTemplate.retryIntervalSeconds" />
- <TextField source="remark" label="table.field.taskPathTemplate.remark" />
- <TextField source="createdBy" label="table.field.taskPathTemplate.createdBy" />
- <TextField source="updatedBy" label="table.field.taskPathTemplate.updatedBy" />
- <DateField source="createdTime" label="table.field.taskPathTemplate.createdTime" showTime />
- <DateField source="updatedTime" label="table.field.taskPathTemplate.updatedTime" showTime />
-
- <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} onOpenFlow={handleOpenFlow} />
</List>
<TaskPathTemplateCreate
open={createDialog}
setOpen={setCreateDialog}
/>
+ <Dialog
+ open={flowDialog}
+ onClose={() => setFlowDialog(false)}
+ fullWidth
+ maxWidth="xl"
+ >
+ <DialogContent>
+ <TaskTemplateFlowViewer record={currentRecord} />
+ </DialogContent>
+ <DialogActions>
+ <Button onClick={() => setFlowDialog(false)} color="primary">
+ {translate('ra.action.close')}
+ </Button>
+ </DialogActions>
+ </Dialog>
<PageDrawer
title='TaskPathTemplate Detail'
drawerVal={drawerVal}
@@ -185,4 +192,82 @@
)
}
+
+const TableItems = ({ drawerVal, onOpenFlow }) => {
+ const { isLoading } = useListContext();
+
+ const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
+
+ // Wait, the above simple Button won't work easily because it doesn't have access to the record of the row being rendered
+ // when defined in this scope. The `rowClick` works because the datagrid calls it with the record.
+ // The `EditButton` works because it uses `useRecordContext`.
+
+ // So I need a custom component for the button that uses `useRecordContext`.
+
+ return (
+ <Box sx={{
+ position: 'relative',
+ maxHeight: boxMaxHeight,
+ maxWidth: boxMaxWidth,
+ overflowX: 'auto',
+ overflowY: 'auto',
+ '& .MuiTableCell-root': {
+ whiteSpace: 'nowrap',
+ }
+ }}>
+ {baseColumns.length > 0 &&
+ <StickyDataTable
+ stickyRight={['opt']}
+ storeKey='taskPathTemplate'
+ bulkActionButtons={false}
+ rowClick={false}
+ hiddenColumns={['id', 'createTime', 'createBy', 'memo', 'statusBool']}
+ >
+ {baseColumns
+ .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>
+ ))
+ }
+ <DataTable.Col
+ source="opt"
+ label="common.field.opt"
+ >
+ <WrapperField source="opt" cellClassName="opt" label="common.field.opt">
+ <ViewFlowButton onClick={onOpenFlow} />
+ <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
+ <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
+ </WrapperField>
+ </DataTable.Col>
+ </StickyDataTable>}
+ </Box>
+ )
+}
+
+import AccountTreeIcon from '@mui/icons-material/AccountTree';
+
+const ViewFlowButton = ({ onClick }) => {
+ const record = useRecordContext();
+ if (!record) return null;
+ return (
+ <Button
+ onClick={(e) => {
+ e.stopPropagation();
+ onClick && onClick(record);
+ }}
+ sx={{ padding: '1px', fontSize: '.75rem', minWidth: 'auto', marginRight: 0.5 }}
+ color="primary"
+ >
+ <AccountTreeIcon />
+ </Button>
+ )
+}
+
+
export default TaskPathTemplateList;
diff --git a/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx b/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx
new file mode 100644
index 0000000..c388420
--- /dev/null
+++ b/rsf-admin/src/page/taskPathTemplate/TaskTemplateFlowViewer.jsx
@@ -0,0 +1,169 @@
+import React, { useState, useEffect } from "react";
+import {
+ useTranslate,
+ useListController,
+ ListContextProvider,
+ TextField,
+ NumberField,
+ Pagination,
+ DataTable,
+} from 'react-admin';
+import { Box, Grid, Card, CardContent, Typography } from '@mui/material';
+import StickyDataTable from "@/page/components/StickyDataTable";
+
+const ViewTable = ({ title, resource, filter, onClick, selectedId, columns }) => {
+ const listContext = useListController({
+ resource: resource,
+ filter: filter,
+ perPage: 100,
+ sort: { field: 'id', order: 'ASC' },
+ disableSyncWithLocation: true,
+ });
+
+ if (!filter) {
+ return (
+ <Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
+ <Box p={1} borderBottom={1} borderColor="divider">
+ <Typography variant="subtitle1">{title}</Typography>
+ </Box>
+ <CardContent sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
+ <Typography variant="body2" color="textSecondary">
+ 璇峰厛閫夋嫨涓婁竴绾ф暟鎹�
+ </Typography>
+ </CardContent>
+ </Card>
+ );
+ }
+
+ return (
+ <ListContextProvider value={listContext}>
+ <Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
+ <Box p={1} borderBottom={1} borderColor="divider">
+ <Typography variant="subtitle1">{title}</Typography>
+ </Box>
+ <Box flexGrow={1} overflow="auto" sx={{
+ '& .MuiTableCell-root': {
+ whiteSpace: 'nowrap',
+ },
+ }}>
+ <StickyDataTable
+ storeKey={`${resource}_flow_view`}
+ bulkActionButtons={false}
+ rowClick={(id, resource, record) => {
+ if (onClick) {
+ onClick(record);
+ }
+ return false;
+ }}
+ sx={{
+ '& .RaDatagrid-row': {
+ cursor: 'pointer',
+ '&:hover': {
+ backgroundColor: 'action.hover',
+ },
+ },
+ '& .RaDatagrid-row.Mui-selected': {
+ backgroundColor: 'action.selected',
+ }
+
+ }}
+ // Manually handle selection style if needed, or rely on StickyDataTable support
+ >
+ {columns.map((col, index) => (
+ <DataTable.Col
+ key={col.props.source}
+ source={col.props.source}
+ label={col.props.label}
+ >
+ {col}
+ </DataTable.Col>
+ ))}
+ </StickyDataTable>
+ </Box>
+ <Pagination rowsPerPageOptions={[10, 25, 50, 100]} />
+ </Card>
+ </ListContextProvider>
+ );
+};
+
+const TaskTemplateFlowViewer = ({ record }) => {
+ const translate = useTranslate();
+ const [selectedNode, setSelectedNode] = useState(null);
+ const [selectedFlow, setSelectedFlow] = useState(null);
+
+ // Reset selection when record changes
+ useEffect(() => {
+ setSelectedNode(null);
+ setSelectedFlow(null);
+ }, [record]);
+
+ const handleNodeClick = (node) => {
+ if (!node) return;
+ setSelectedNode(node);
+ setSelectedFlow(null);
+ };
+
+ const handleFlowClick = (flow) => {
+ if (!flow) return;
+ setSelectedFlow(flow);
+ };
+
+ const nodeColumns = [
+ <NumberField source="nodeOrder" label="table.field.taskPathTemplateNode.nodeOrder" key="nodeOrder" />,
+ <TextField source="nodeCode" label="table.field.taskPathTemplateNode.nodeCode" key="nodeCode" />,
+ <TextField source="nodeName" label="table.field.taskPathTemplateNode.nodeName" key="nodeName" />,
+ <TextField source="systemCode" label="table.field.taskPathTemplateNode.systemCode" key="systemCode" />,
+ ];
+
+ const flowColumns = [
+ <TextField source="flowCode" label="table.field.subsystemFlowTemplate.flowCode" key="flowCode" />,
+ <TextField source="flowName" label="table.field.subsystemFlowTemplate.flowName" key="flowName" />,
+ <TextField source="systemCode" label="table.field.subsystemFlowTemplate.systemCode" key="systemCode" />,
+ ];
+
+ const stepColumns = [
+ <NumberField source="stepOrder" label="table.field.flowStepTemplate.stepOrder" key="stepOrder" />,
+ <TextField source="stepCode" label="table.field.flowStepTemplate.stepCode" key="stepCode" />,
+ <TextField source="stepName" label="table.field.flowStepTemplate.stepName" key="stepName" />,
+ <TextField source="stepType" label="table.field.flowStepTemplate.stepType" key="stepType" />,
+ ];
+
+ if (!record) return null;
+
+ return (
+ <Box sx={{ height: '80vh', p: 1, backgroundColor: '#f0f0f0' }}>
+ <Grid container spacing={1} sx={{ height: '100%' }}>
+ <Grid item xs={4} sx={{ height: '100%' }}>
+ <ViewTable
+ title={translate('menu.taskPathTemplateNode')}
+ resource="taskPathTemplateNode"
+ filter={{ templateId: record.id }}
+ onClick={handleNodeClick}
+ selectedId={selectedNode?.id}
+ columns={nodeColumns}
+ />
+ </Grid>
+ <Grid item xs={4} sx={{ height: '100%' }}>
+ <ViewTable
+ title={translate('menu.subsystemFlowTemplate')}
+ resource="subsystemFlowTemplate"
+ filter={selectedNode ? { systemCode: selectedNode.systemCode } : null}
+ onClick={handleFlowClick}
+ selectedId={selectedFlow?.id}
+ columns={flowColumns}
+ />
+ </Grid>
+ <Grid item xs={4} sx={{ height: '100%' }}>
+ <ViewTable
+ title={translate('menu.flowStepTemplate')}
+ resource="flowStepTemplate"
+ filter={selectedFlow ? { flowId: selectedFlow.id } : null}
+ columns={stepColumns}
+ />
+ </Grid>
+ </Grid>
+ </Box>
+ );
+};
+
+export default TaskTemplateFlowViewer;
diff --git a/rsf-admin/src/utils/useTableLayout.js b/rsf-admin/src/utils/useTableLayout.js
new file mode 100644
index 0000000..bbd1707
--- /dev/null
+++ b/rsf-admin/src/utils/useTableLayout.js
@@ -0,0 +1,33 @@
+
+import { useSidebarState } from 'react-admin';
+import { PAGE_DRAWER_WIDTH } from '@/config/setting';
+
+/**
+ * useTableLayout Hook
+ *
+ * 缁熶竴璁$畻鍖呭惈渚ц竟鏍忋�佸彸渚ф娊灞夌殑琛ㄦ牸瀹瑰櫒鏈�澶у楂樸��
+ *
+ * @param {boolean} drawerOpen - 鍙充晶鎶藉眽鏄惁鎵撳紑
+ * @param {number} [customHeightOffset=210] - 鑷畾涔夐珮搴︽墸闄ゅ亸绉婚噺 (Header + Toolbar + Filters + Pagination + Padding)
+ * @returns {Object} { boxMaxWidth, boxMaxHeight } - 璁$畻鍚庣殑鏈�澶у搴﹀拰楂樺害鏍峰紡鍊�
+ */
+export const useTableLayout = (drawerOpen = false, customHeightOffset = 210) => {
+ const [sidebarIsOpen] = useSidebarState();
+
+ const sidebarWidth = sidebarIsOpen ? 200 : 50;
+ const contentPadding = 10; // 棰勭暀杈硅窛
+ const rightDrawerWidth = drawerOpen ? PAGE_DRAWER_WIDTH : 0;
+
+ // 璁$畻鏈�澶у搴︼細瑙嗗彛瀹藉害 - (渚ц竟鏍� + 鍙充晶鎶藉眽 + 杈硅窛)
+ const boxMaxWidth = `calc(100vw - ${sidebarWidth + rightDrawerWidth + contentPadding}px)`;
+
+ // 璁$畻鏈�澶ч珮搴︼細瑙嗗彛楂樺害 - 椤堕儴鍜屽叾浠朥I鍏冪礌鐨勯珮搴�
+ const boxMaxHeight = `calc(100vh - ${customHeightOffset}px)`;
+
+ return {
+ boxMaxWidth,
+ boxMaxHeight
+ };
+};
+
+export default useTableLayout;
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/FlowStepTemplateController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/FlowStepTemplateController.java
index 9ac3230..0b27bd8 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/FlowStepTemplateController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/FlowStepTemplateController.java
@@ -25,7 +25,6 @@
@Autowired
private FlowStepTemplateService flowStepTemplateService;
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@PostMapping("/flowStepTemplate/page")
public R page(@RequestBody Map<String, Object> map) {
BaseParam baseParam = buildParam(map, BaseParam.class);
@@ -33,25 +32,21 @@
return R.ok().add(flowStepTemplateService.page(pageParam, pageParam.buildWrapper(true)));
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@PostMapping("/flowStepTemplate/list")
public R list(@RequestBody Map<String, Object> map) {
return R.ok().add(flowStepTemplateService.list());
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@PostMapping({"/flowStepTemplate/many/{ids}", "/flowStepTemplates/many/{ids}"})
public R many(@PathVariable Long[] ids) {
return R.ok().add(flowStepTemplateService.listByIds(Arrays.asList(ids)));
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@GetMapping("/flowStepTemplate/{id}")
public R get(@PathVariable("id") Long id) {
return R.ok().add(flowStepTemplateService.getById(id));
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:save')")
@OperationLog("Create 瀛愭祦绋嬫楠ゆā鏉�")
@PostMapping("/flowStepTemplate/save")
public R save(@RequestBody FlowStepTemplate flowStepTemplate) {
@@ -65,7 +60,6 @@
return R.ok("Save Success").add(flowStepTemplate);
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:update')")
@OperationLog("Update 瀛愭祦绋嬫楠ゆā鏉�")
@PostMapping("/flowStepTemplate/update")
public R update(@RequestBody FlowStepTemplate flowStepTemplate) {
@@ -77,7 +71,6 @@
return R.ok("Update Success").add(flowStepTemplate);
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:remove')")
@OperationLog("Delete 瀛愭祦绋嬫楠ゆā鏉�")
@PostMapping("/flowStepTemplate/remove/{ids}")
public R remove(@PathVariable Long[] ids) {
@@ -87,7 +80,6 @@
return R.ok("Delete Success").add(ids);
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@PostMapping("/flowStepTemplate/query")
public R query(@RequestParam(required = false) String condition) {
List<KeyValVo> vos = new ArrayList<>();
@@ -101,7 +93,6 @@
return R.ok().add(vos);
}
- @PreAuthorize("hasAuthority('system:flowStepTemplate:list')")
@PostMapping("/flowStepTemplate/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
ExcelUtil.build(ExcelUtil.create(flowStepTemplateService.list(), FlowStepTemplate.class), response);
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/SubsystemFlowTemplateController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/SubsystemFlowTemplateController.java
index 5aae0cc..60d3578 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/SubsystemFlowTemplateController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/SubsystemFlowTemplateController.java
@@ -25,7 +25,6 @@
@Autowired
private SubsystemFlowTemplateService subsystemFlowTemplateService;
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@PostMapping("/subsystemFlowTemplate/page")
public R page(@RequestBody Map<String, Object> map) {
BaseParam baseParam = buildParam(map, BaseParam.class);
@@ -33,25 +32,21 @@
return R.ok().add(subsystemFlowTemplateService.page(pageParam, pageParam.buildWrapper(true)));
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@PostMapping("/subsystemFlowTemplate/list")
public R list(@RequestBody Map<String, Object> map) {
return R.ok().add(subsystemFlowTemplateService.list());
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@PostMapping({"/subsystemFlowTemplate/many/{ids}", "/subsystemFlowTemplates/many/{ids}"})
public R many(@PathVariable Long[] ids) {
return R.ok().add(subsystemFlowTemplateService.listByIds(Arrays.asList(ids)));
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@GetMapping("/subsystemFlowTemplate/{id}")
public R get(@PathVariable("id") Long id) {
return R.ok().add(subsystemFlowTemplateService.getById(id));
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:save')")
@OperationLog("Create 瀛愮郴缁熸ā鏉�")
@PostMapping("/subsystemFlowTemplate/save")
public R save(@RequestBody SubsystemFlowTemplate subsystemFlowTemplate) {
@@ -65,7 +60,6 @@
return R.ok("Save Success").add(subsystemFlowTemplate);
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:update')")
@OperationLog("Update 瀛愮郴缁熸ā鏉�")
@PostMapping("/subsystemFlowTemplate/update")
public R update(@RequestBody SubsystemFlowTemplate subsystemFlowTemplate) {
@@ -77,7 +71,6 @@
return R.ok("Update Success").add(subsystemFlowTemplate);
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:remove')")
@OperationLog("Delete 瀛愮郴缁熸ā鏉�")
@PostMapping("/subsystemFlowTemplate/remove/{ids}")
public R remove(@PathVariable Long[] ids) {
@@ -87,7 +80,6 @@
return R.ok("Delete Success").add(ids);
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@PostMapping("/subsystemFlowTemplate/query")
public R query(@RequestParam(required = false) String condition) {
List<KeyValVo> vos = new ArrayList<>();
@@ -101,7 +93,6 @@
return R.ok().add(vos);
}
- @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:list')")
@PostMapping("/subsystemFlowTemplate/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
ExcelUtil.build(ExcelUtil.create(subsystemFlowTemplateService.list(), SubsystemFlowTemplate.class), response);
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/TaskPathTemplateNodeController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/TaskPathTemplateNodeController.java
index 66a99c3..3d1f6f6 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/TaskPathTemplateNodeController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/TaskPathTemplateNodeController.java
@@ -25,7 +25,7 @@
@Autowired
private TaskPathTemplateNodeService taskPathTemplateNodeService;
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@PostMapping("/taskPathTemplateNode/page")
public R page(@RequestBody Map<String, Object> map) {
BaseParam baseParam = buildParam(map, BaseParam.class);
@@ -33,25 +33,25 @@
return R.ok().add(taskPathTemplateNodeService.page(pageParam, pageParam.buildWrapper(true)));
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@PostMapping("/taskPathTemplateNode/list")
public R list(@RequestBody Map<String, Object> map) {
return R.ok().add(taskPathTemplateNodeService.list());
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@PostMapping({"/taskPathTemplateNode/many/{ids}", "/taskPathTemplateNodes/many/{ids}"})
public R many(@PathVariable Long[] ids) {
return R.ok().add(taskPathTemplateNodeService.listByIds(Arrays.asList(ids)));
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@GetMapping("/taskPathTemplateNode/{id}")
public R get(@PathVariable("id") Long id) {
return R.ok().add(taskPathTemplateNodeService.getById(id));
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:save')")
+
@OperationLog("Create 鐗╂枡鏉冮檺")
@PostMapping("/taskPathTemplateNode/save")
public R save(@RequestBody TaskPathTemplateNode taskPathTemplateNode) {
@@ -65,7 +65,7 @@
return R.ok("Save Success").add(taskPathTemplateNode);
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:update')")
+
@OperationLog("Update 鐗╂枡鏉冮檺")
@PostMapping("/taskPathTemplateNode/update")
public R update(@RequestBody TaskPathTemplateNode taskPathTemplateNode) {
@@ -77,7 +77,7 @@
return R.ok("Update Success").add(taskPathTemplateNode);
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:remove')")
+
@OperationLog("Delete 鐗╂枡鏉冮檺")
@PostMapping("/taskPathTemplateNode/remove/{ids}")
public R remove(@PathVariable Long[] ids) {
@@ -87,7 +87,7 @@
return R.ok("Delete Success").add(ids);
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@PostMapping("/taskPathTemplateNode/query")
public R query(@RequestParam(required = false) String condition) {
List<KeyValVo> vos = new ArrayList<>();
@@ -101,7 +101,7 @@
return R.ok().add(vos);
}
- @PreAuthorize("hasAuthority('system:taskPathTemplateNode:list')")
+
@PostMapping("/taskPathTemplateNode/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
ExcelUtil.build(ExcelUtil.create(taskPathTemplateNodeService.list(), TaskPathTemplateNode.class), response);
--
Gitblit v1.9.1