| | |
| | | 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' |
| | |
| | | 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: '目标库位', |
| | |
| | | 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) { |
| | |
| | | return preparation; |
| | | case 'menuPda': |
| | | return menuPda; |
| | | case 'taskPathTemplate': |
| | | return taskPathTemplate; |
| | | // case "locItem": |
| | | // return locItem; |
| | | default: |
| New file |
| | |
| | | |
| | | 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 包裹的列也能被处理 |
| | | 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; |
| | |
| | | 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'; |
| | |
| | | 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]' |
| | | ]; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | 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', |
| | |
| | | /> |
| | | )} |
| | | {columns.length > 0 && |
| | | <DataTable |
| | | <StickyDataTable |
| | | stickyRight={['createTime']} |
| | | storeKey='asnOrderItem' |
| | | bulkActionButtons={false} |
| | | rowClick={false} |
| | |
| | | key={column.key || column.props.source} |
| | | source={column.props.source} |
| | | label={column.props.label} |
| | | sx={column.props.sx} |
| | | > |
| | | {column} |
| | | </DataTable.Col> |
| | | )) |
| | | } |
| | | </DataTable>} |
| | | </StickyDataTable>} |
| | | </Box> |
| | | ) |
| | | } |
| | |
| | | import { useNavigate } from 'react-router-dom'; |
| | | import { |
| | | List, |
| | | DataTable, |
| | | DatagridConfigurable, |
| | | SearchInput, |
| | | TopToolbar, |
| | | SelectColumnsButton, |
| | | ColumnsButton, |
| | | EditButton, |
| | | FilterButton, |
| | | CreateButton, |
| | |
| | | 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 />, |
| | |
| | | />, |
| | | ] |
| | | |
| | | 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"> |
| | |
| | | <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} |
| | |
| | | ) |
| | | } |
| | | |
| | | |
| | | 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; |
| New file |
| | |
| | | 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; |
| New file |
| | |
| | | |
| | | 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)`; |
| | | |
| | | // 计算最大高度:视口高度 - 顶部和其他UI元素的高度 |
| | | const boxMaxHeight = `calc(100vh - ${customHeightOffset}px)`; |
| | | |
| | | return { |
| | | boxMaxWidth, |
| | | boxMaxHeight |
| | | }; |
| | | }; |
| | | |
| | | export default useTableLayout; |
| | |
| | | @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); |
| | |
| | | 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) { |
| | |
| | | return R.ok("Save Success").add(flowStepTemplate); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:flowStepTemplate:update')") |
| | | @OperationLog("Update 子流程步骤模板") |
| | | @PostMapping("/flowStepTemplate/update") |
| | | public R update(@RequestBody FlowStepTemplate flowStepTemplate) { |
| | |
| | | return R.ok("Update Success").add(flowStepTemplate); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:flowStepTemplate:remove')") |
| | | @OperationLog("Delete 子流程步骤模板") |
| | | @PostMapping("/flowStepTemplate/remove/{ids}") |
| | | public R remove(@PathVariable Long[] ids) { |
| | |
| | | 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<>(); |
| | |
| | | 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); |
| | |
| | | @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); |
| | |
| | | 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) { |
| | |
| | | return R.ok("Save Success").add(subsystemFlowTemplate); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:update')") |
| | | @OperationLog("Update 子系统模板") |
| | | @PostMapping("/subsystemFlowTemplate/update") |
| | | public R update(@RequestBody SubsystemFlowTemplate subsystemFlowTemplate) { |
| | |
| | | return R.ok("Update Success").add(subsystemFlowTemplate); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:subsystemFlowTemplate:remove')") |
| | | @OperationLog("Delete 子系统模板") |
| | | @PostMapping("/subsystemFlowTemplate/remove/{ids}") |
| | | public R remove(@PathVariable Long[] ids) { |
| | |
| | | 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<>(); |
| | |
| | | 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); |
| | |
| | | @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); |
| | |
| | | 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) { |
| | |
| | | return R.ok("Save Success").add(taskPathTemplateNode); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateNode:update')") |
| | | |
| | | @OperationLog("Update 物料权限") |
| | | @PostMapping("/taskPathTemplateNode/update") |
| | | public R update(@RequestBody TaskPathTemplateNode taskPathTemplateNode) { |
| | |
| | | return R.ok("Update Success").add(taskPathTemplateNode); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:taskPathTemplateNode:remove')") |
| | | |
| | | @OperationLog("Delete 物料权限") |
| | | @PostMapping("/taskPathTemplateNode/remove/{ids}") |
| | | public R remove(@PathVariable Long[] ids) { |
| | |
| | | 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<>(); |
| | |
| | | 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); |