skyouc
2025-04-25 bcf39531dcfaea85297312405fbecc7eebccf7d0
#新增
1. 新增波次列表及详情
2. 新增立库站点绑定
16个文件已修改
37个文件已添加
3412 ■■■■■ 已修改文件
rsf-admin/src/i18n/en.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/ResourceContent.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/deviceSite/DeviceSiteList.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/deviceSite/InitModal.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/LocList.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindCreate.jsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindEdit.jsx 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindList.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindPanel.jsx 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/WaveCreate.jsx 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/WaveEdit.jsx 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/WaveList.jsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/WavePanel.jsx 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/wave/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemCreate.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemEdit.jsx 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemList.jsx 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemPanel.jsx 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveItemController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/DeviceSiteParame.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceSite.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Wave.java 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/WaveExceStatus.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/DeviceBindMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveItemMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/DeviceBindService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveItemService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceBindServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceSiteServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveItemServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/deviceBind.sql 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/wave.sql 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/waveItem.sql 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/DeviceBindMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/WaveItemMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/WaveMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -70,6 +70,7 @@
            loadMore: 'Load More Data',
            complete: 'Complete',
            deprecate: 'Deprecate',
            inputPlaceholder: 'Use commas to separate',
            resend: 'RESEND',
            selected: 'selected',
            batch: 'batch'
@@ -175,6 +176,7 @@
        outStockItem: 'Out Stock Item',
        inStockPoces: 'In Stock Pocess',
        outStockPoces: 'Out Stock Pocess',
        deviceBind: 'Device Bind',
    },
    table: {
        field: {
@@ -616,6 +618,7 @@
            },
            deliveryItem: {
                deliveryId: "deliveryId",
                deliveryCode: "Delivery Code",
                platItemId: "platItemId",
                matnrCode: "matnrCode",
                matnrName: "matnrName",
@@ -667,7 +670,6 @@
                deviceCode: "deviceCode",
                deviceSite: "deviceSite",
                flagInit: "flagInit",
                sites: "sites",
            },
            waitPakin: {
                code: "code",
@@ -833,6 +835,18 @@
                model: "model",
                fieldsIndex: "fieldsIndex",
            },
            deviceBind: {
                currentRow: "currentRow",
                startRow: "startRow",
                endRow: "endRow",
                deviceQty: "deviceQty",
                startDeviceNo: "startDeviceNo",
                endDeviceNo: "endDeviceNo",
                staList: "staList",
                typeId: "typeId",
                beSimilar: "beSimilar",
                emptySimilar: "emptySimilar",
            },
        }
    },
    page: {
@@ -944,6 +958,7 @@
        asnCreate: "Create By Order",
        createTask: "createTask",
        recover: "recover",
        createWave: "Create Wave",
        order: 'Orders',
    },
rsf-admin/src/i18n/zh.js
@@ -58,6 +58,7 @@
            collapse: '折叠',
            collapseAll: '全部折叠',
            scope: '权限',
            inputPlaceholder: '站点可填多个,中间以英文逗号区分(,)',
            import: {
                title: '导入',
                stop: '停止导入',
@@ -176,6 +177,7 @@
        outStockItem: '出库单明细',
        inStockPoces: '入库流程',
        outStockPoces: '出库流程',
        deviceBind: '设备绑定',
    },
    table: {
        field: {
@@ -361,8 +363,8 @@
            loc: {
                warehouseId: "所属仓库",
                areaId: "所属库区",
                code: "库位编码",
                type: "库位类型",
                code: "编码",
                type: "类型",
                name: "名称",
                flagLogic: "虚拟库位",
                fucAtrrs: "功能属性",
@@ -661,6 +663,7 @@
            },
            deliveryItem: {
                deliveryId: "主单ID",
                deliveryCode: "单号",
                platItemId: "行号",
                matnrCode: "物料编码",
                matnrName: "物料名称",
@@ -704,15 +707,14 @@
            },
            deviceSite: {
                type: "入出库类型",
                site: "站点编号",
                site: "作业站点",
                name: "名称",
                wcsCode: "wcs站点编号",
                target: "目标站点",
                label: "站点标签",
                device: "立库类型",
                deviceCode: "设备编号",
                deviceSite: "设备站点",
                flagInit: "是否初始化",
                sites: "作业站点",
            },
            waitPakin: {
                code: "编码",
@@ -878,6 +880,18 @@
                model: "型号",
                fieldsIndex: "动态索引",
            },
            deviceBind: {
                currentRow: "当前排号",
                startRow: "起始排号",
                endRow: "终止排号",
                deviceQty: "设备数量",
                startDeviceNo: "起始设备号",
                endDeviceNo: "终止设备号",
                staList: "站点集",
                typeId: "库区类型",
                beSimilar: "相似开关",
                emptySimilar: "空板靠近开关",
            },
        }
    },
    page: {
@@ -991,7 +1005,8 @@
        complete: "完成",
        close: "关闭",
        asnCreate: "通过单据创建",
        createTask: "生成任务",
        createTask: "下发任务",
        createWave: "生成波次",
        recover: "继续收货",
    },
};
rsf-admin/src/page/ResourceContent.js
@@ -43,6 +43,7 @@
import stock from './orders/stock';
import delivery from './orders/delivery';
import outStock from './orders/outStock';
import deviceBind from './deviceBind';
const ResourceContent = (node) => {
@@ -125,6 +126,8 @@
            return delivery;
        case 'outStock':
            return outStock;
        case 'deviceBind':
            return deviceBind;
        default:
            return {
                list: ListGuesser,
rsf-admin/src/page/basicInfo/deviceSite/DeviceSiteList.jsx
@@ -148,18 +148,17 @@
                    preferenceKey='deviceSite'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                    omit={['id', 'createTime', 'createBy', 'memo', 'label']}
                >
                    <NumberField source="id" />
                    <TextField source="name" label="table.field.deviceSite.name" />
                    <NumberField source="type$" label="table.field.deviceSite.type" />
                    <TextField source="site" label="table.field.deviceSite.site" />
                    <TextField source="wcsCode" label="table.field.deviceSite.wcsCode" />
                    <TextField source="target" label="table.field.deviceSite.target" />
                    <TextField source="label" label="table.field.deviceSite.label" />
                    <TextField source="device$" label="table.field.deviceSite.device" />
                    <TextField source="deviceCode" label="table.field.deviceSite.deviceCode" />
                    <TextField source="deviceSite" label="table.field.deviceSite.deviceSite" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
rsf-admin/src/page/basicInfo/deviceSite/InitModal.jsx
@@ -95,7 +95,7 @@
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
        <Dialog open={open} maxWidth="lg" fullWidth>
            <Form onSubmit={handleSubmit}>
                <DialogCloseButton onClose={handleClose} />
                <DialogTitle>{translate('toolbar.siteInit')}</DialogTitle>
@@ -119,8 +119,6 @@
                                    dictTypeCode="sys_device_type"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.deviceCode")}
@@ -129,25 +127,33 @@
                                    type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.deviceSite")}
                                    name="deviceSites"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                    type="number"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.sites")}
                                    name="sites"
                                    label={translate("table.field.deviceSite.site")}
                                    name="site"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                    type="number"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.target")}
                                    name="target"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <SelectInput
                                    label="table.field.deviceSite.flagInit"
rsf-admin/src/page/basicInfo/loc/LocList.jsx
@@ -66,20 +66,20 @@
    '& .column-name': {
    },
    '& .opt': {
        width: 200
        width: 180
    },
    '& .RaDatagrid-headerCell': {
        textAlign: 'left'
    },
    '& .RaDatagrid-rowCell': {
        textAlign: 'left'
    },
    // '& .RaDatagrid-headerCell': {
    //     textAlign: 'left'
    // },
    // '& .RaDatagrid-rowCell': {
    //     textAlign: 'left'
    // },
    '& .type .MuiTypography-root': {
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        display: 'block',
        width: '200px',
        width: 'auto',
    },
}));
rsf-admin/src/page/deviceBind/DeviceBindCreate.jsx
New file
@@ -0,0 +1,174 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const DeviceBindCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.currentRow"
                                        source="currentRow"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.startRow"
                                        source="startRow"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.endRow"
                                        source="endRow"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.deviceQty"
                                        source="deviceQty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.startDeviceNo"
                                        source="startDeviceNo"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.endDeviceNo"
                                        source="endDeviceNo"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.staList"
                                        source="staList"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.typeId"
                                        source="typeId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.beSimilar"
                                        source="beSimilar"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.emptySimilar"
                                        source="emptySimilar"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
export default DeviceBindCreate;
rsf-admin/src/page/deviceBind/DeviceBindEdit.jsx
New file
@@ -0,0 +1,146 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const DeviceBindEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.currentRow"
                                source="currentRow"
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.startRow"
                                source="startRow"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.endRow"
                                source="endRow"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.deviceQty"
                                source="deviceQty"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.startDeviceNo"
                                source="startDeviceNo"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.endDeviceNo"
                                source="endDeviceNo"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.staList"
                                source="staList"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.typeId"
                                source="typeId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.beSimilar"
                                source="beSimilar"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.emptySimilar"
                                source="emptySimilar"
                                parse={v => v}
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default DeviceBindEdit;
rsf-admin/src/page/deviceBind/DeviceBindList.jsx
New file
@@ -0,0 +1,170 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    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,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import DeviceBindCreate from "./DeviceBindCreate";
import DeviceBindPanel from "./DeviceBindPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="currentRow" label="table.field.deviceBind.currentRow" />,
    <NumberInput source="startRow" label="table.field.deviceBind.startRow" />,
    <NumberInput source="endRow" label="table.field.deviceBind.endRow" />,
    <NumberInput source="deviceQty" label="table.field.deviceBind.deviceQty" />,
    <NumberInput source="startDeviceNo" label="table.field.deviceBind.startDeviceNo" />,
    <NumberInput source="endDeviceNo" label="table.field.deviceBind.endDeviceNo" />,
    <TextInput source="staList" label="table.field.deviceBind.staList" />,
    <NumberInput source="typeId" label="table.field.deviceBind.typeId" />,
    <TextInput source="beSimilar" label="table.field.deviceBind.beSimilar" />,
    <TextInput source="emptySimilar" label="table.field.deviceBind.emptySimilar" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
        resettable
    />,
]
const DeviceBindList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.deviceBind"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='deviceBind' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='deviceBind'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <DeviceBindPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="currentRow" label="table.field.deviceBind.currentRow" />
                    <NumberField source="startRow" label="table.field.deviceBind.startRow" />
                    <NumberField source="endRow" label="table.field.deviceBind.endRow" />
                    <NumberField source="deviceQty" label="table.field.deviceBind.deviceQty" />
                    <NumberField source="startDeviceNo" label="table.field.deviceBind.startDeviceNo" />
                    <NumberField source="endDeviceNo" label="table.field.deviceBind.endDeviceNo" />
                    <TextField source="staList" label="table.field.deviceBind.staList" />
                    <NumberField source="typeId" label="table.field.deviceBind.typeId" />
                    <TextField source="beSimilar" label="table.field.deviceBind.beSimilar" />
                    <TextField source="emptySimilar" label="table.field.deviceBind.emptySimilar" />
                    <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>
            </List>
            <DeviceBindCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='DeviceBind Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default DeviceBindList;
rsf-admin/src/page/deviceBind/DeviceBindPanel.jsx
New file
@@ -0,0 +1,111 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const DeviceBindPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.deviceBind.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.currentRow"
                                property={record.currentRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.startRow"
                                property={record.startRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.endRow"
                                property={record.endRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.deviceQty"
                                property={record.deviceQty}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.startDeviceNo"
                                property={record.startDeviceNo}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.endDeviceNo"
                                property={record.endDeviceNo}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.staList"
                                property={record.staList}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.typeId"
                                property={record.typeId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.beSimilar"
                                property={record.beSimilar}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.emptySimilar"
                                property={record.emptySimilar}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default DeviceBindPanel;
rsf-admin/src/page/deviceBind/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import DeviceBindList from "./DeviceBindList";
import DeviceBindEdit from "./DeviceBindEdit";
export default {
    list: DeviceBindList,
    edit: DeviceBindEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -55,6 +55,7 @@
import OutOrderCreate from "./OutOrderCreate";
import AddIcon from '@mui/icons-material/Add';
import OutOrderModal from "./OutOrderModal";
import PublicIcon from '@mui/icons-material/Public';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
  '& .css-1vooibu-MuiSvgIcon-root': {
@@ -141,7 +142,7 @@
            <MyCreateButton onClick={() => { setManualDialog(true) }} />
            <SelectColumnsButton preferenceKey='outStock' />
            <ImportButton value={'asnOrderItem'} />
            <MyExportButton />
            {/* <MyExportButton /> */}
          </TopToolbar>
        )}
        perPage={DEFAULT_PAGE_SIZE}
@@ -151,6 +152,7 @@
          preferenceKey='outStock'
          bulkActionButtons={
            <>
              <PublicTaskButton />
              <MyExportButton />
              <BulkDeleteButton mutationMode={OPERATE_MODE}
              />
@@ -201,6 +203,25 @@
}
export default OutOrderList;
const PublicTaskButton = () => {
  const record = useRecordContext();
  const { selectedIds, onUnselectItems } = useListContext();
  const pubClick = (event) => {
    event.stopPropagation();
    onUnselectItems();
  }
  return (
    <Button
      onClick={pubClick}
      label={"toolbar.createWave"}
      startIcon={<PublicIcon />}
    />);
}
const MyButton = ({ setCreateDialog, setmodalType }) => {
  const record = useRecordContext();
  const handleEditClick = (btn) => {
@@ -227,8 +248,16 @@
  const refresh = useRefresh();
  const createByOrder = async (event) => {
    const {selectedIds, onUnselectItems} = useListContext();
    event.stopPropagation();
    setCreateDialog(true);
    const res = await request.post(`/outStock/generate/wave`, { ids: selectedIds });
    if (res?.data?.code === 200) {
      notify(res.data.msg);
    } else {
      notify(res.data.msg);
    }
  }
  return (
@@ -241,7 +270,6 @@
  const record = useRecordContext();
  const notify = useNotify();
  const refresh = useRefresh();
  console.log(record);
  const cancelOrder = async () => {
    const { data: { code, data, msg } } = await request.get(`/outStock/cancel/${record?.id}`);
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx
@@ -54,14 +54,6 @@
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 220
    },
    '& .wkType': {
        width: 110
    },
    '& .status': {
        width: 90
    },
@@ -102,9 +94,10 @@
        const { filterValues, setFilters, refetch } = useListContext('deliveryItem');
        const [formValues, setFormValues] = useState(filterValues);
        const handleChange = (event) => {
            if (event.target == undefined || event.target == null) {return}
            setFormValues(formValues => ({
                ...formValues,
                [event.target.name]: event.target.value
              [event.target.name]: event.target.value
            }));
        };
@@ -125,10 +118,22 @@
                    </Stack>
                    <Stack>
                        <TextInput
                            source="deliveryCode"
                            label="table.field.deliveryItem.deliveryCode"
                            defaultValue={params?.matnrName}
                            onChange={handleChange}
                            resettable
                        />
                    </Stack>
                    <Stack>
                        <TextInput
                            source="matnrName"
                            label="table.field.deliveryItem.matnrName"
                            defaultValue={params?.matnrName}
                            onChange={handleChange}
                            resettable
                        />
                    </Stack>
                    <Stack>
@@ -136,6 +141,7 @@
                            source="matnrCode"
                            label="table.field.deliveryItem.matnrCode"
                            defaultValue={params?.matnrCode}
                            resettable
                            onChange={handleChange} />
                    </Stack>
                    <Stack>
@@ -143,6 +149,7 @@
                            source="splrName"
                            label="table.field.deliveryItem.splrName"
                            defaultValue={params?.splrName}
                            resettable
                            onChange={handleChange} />
                    </Stack>
                </Grid>
@@ -162,7 +169,7 @@
            aria-hidden
            fullWidth
            disableRestoreFocus
            maxWidth="lg"
            maxWidth="xl"
        >
            <DialogTitle id="form-dialog-title" sx={{
                position: 'sticky',
@@ -200,14 +207,16 @@
                            perPage={DEFAULT_PAGE_SIZE}
                        >
                            <StyledDatagrid
                                sx={{ height: '400' }}
                                preferenceKey='deliveryItem'
                                bulkActionButtons={<AddOutStockButton  setOpen={setOpen}/>}
                                bulkActionButtons={<AddOutStockButton setOpen={setOpen} />}
                                rowClick={(id, resource, record) => false}
                                expand={false}
                                expandSingle={true}
                                omit={['id', 'createTime', 'createBy', 'memo', 'workQty', 'startTime', 'endTime', 'updateBy', 'createTime']}
                            >
                                <NumberField source="id" />
                                <TextField source="deliveryCode" label="table.field.deliveryItem.deliveryCode" />
                                <TextField source="matnrCode" label="table.field.deliveryItem.matnrCode" />
                                <TextField source="matnrName" label="table.field.deliveryItem.matnrName" />
                                <TextField source="unit" label="table.field.deliveryItem.unit" />
@@ -236,8 +245,7 @@
    const { selectedIds, onUnselectItems } = useListContext();
    const notify = useNotify();
    const confirm = async (event) => {
        console.log(selectedIds);
        const res = await request.post(`/outStock/generate/orders`, {ids: selectedIds});
        const res = await request.post(`/outStock/generate/orders`, { ids: selectedIds });
        if (res?.data?.code === 200) {
            notify(res.data.msg);
        } else {
rsf-admin/src/page/wave/WaveCreate.jsx
New file
@@ -0,0 +1,158 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const WaveCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.wave.code"
                                        source="code"
                                        parse={v => v}
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.wave.type"
                                        source="type"
                                        choices={[
                                            { id: 0, name: '手动' },
                                            { id: 1, name: '自动' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.wave.exceStatus"
                                        source="exceStatus"
                                        choices={[
                                            { id: 0, name: '初始化' },
                                            { id: 1, name: '生成任务' },
                                            { id: 2, name: '任务播种' },
                                            { id: 3, name: '完成' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.qty"
                                        source="qty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.orderNum"
                                        source="orderNum"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
export default WaveCreate;
rsf-admin/src/page/wave/WaveEdit.jsx
New file
@@ -0,0 +1,130 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.wave.code"
                                source="code"
                                parse={v => v}
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.wave.type"
                                source="type"
                                choices={[
                                    { id: 0, name: '手动' },
                                    { id: 1, name: '自动' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.wave.exceStatus"
                                source="exceStatus"
                                choices={[
                                    { id: 0, name: '初始化' },
                                    { id: 1, name: '生成任务' },
                                    { id: 2, name: '任务播种' },
                                    { id: 3, name: '完成' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.wave.anfme"
                                source="anfme"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.wave.qty"
                                source="qty"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.wave.orderNum"
                                source="orderNum"
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default WaveEdit;
rsf-admin/src/page/wave/WaveList.jsx
New file
@@ -0,0 +1,174 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    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,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveCreate from "./WaveCreate";
import WavePanel from "./WavePanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="code" label="table.field.wave.code" />,
    <SelectInput source="type" label="table.field.wave.type"
        choices={[
            { id: 0, name: '手动' },
            { id: 1, name: '自动' },
        ]}
    />,
    <SelectInput source="exceStatus" label="table.field.wave.exceStatus"
        choices={[
            { id: 0, name: '初始化' },
            { id: 1, name: '生成任务' },
            { id: 2, name: '任务播种' },
            { id: 3, name: '完成' },
        ]}
    />,
    <NumberInput source="anfme" label="table.field.wave.anfme" />,
    <NumberInput source="qty" label="table.field.wave.qty" />,
    <NumberInput source="orderNum" label="table.field.wave.orderNum" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
        resettable
    />,
]
const WaveList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.wave"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='wave' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='wave'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <WavePanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.wave.code" />
                    <TextField source="type$" label="table.field.wave.type" sortable={false} />
                    <TextField source="exceStatus$" label="table.field.wave.exceStatus" sortable={false} />
                    <NumberField source="anfme" label="table.field.wave.anfme" />
                    <NumberField source="qty" label="table.field.wave.qty" />
                    <NumberField source="orderNum" label="table.field.wave.orderNum" />
                    <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>
            </List>
            <WaveCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='Wave Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveList;
rsf-admin/src/page/wave/WavePanel.jsx
New file
@@ -0,0 +1,87 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const WavePanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.wave.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.code"
                                property={record.code}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.type"
                                property={record.type$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.exceStatus"
                                property={record.exceStatus$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.anfme"
                                property={record.anfme}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.qty"
                                property={record.qty}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.orderNum"
                                property={record.orderNum}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default WavePanel;
rsf-admin/src/page/wave/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import WaveList from "./WaveList";
import WaveEdit from "./WaveEdit";
export default {
    list: WaveList,
    edit: WaveEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-admin/src/page/waveItem/WaveItemCreate.jsx
New file
@@ -0,0 +1,204 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    Box,
} from '@mui/material';
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const WaveItemCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
export default WaveItemCreate;
rsf-admin/src/page/waveItem/WaveItemEdit.jsx
New file
@@ -0,0 +1,176 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveItemEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.waveId"
                                source="waveId"
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.waveCode"
                                source="waveCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.matnrId"
                                source="matnrId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.matnrName"
                                source="matnrName"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.matnrCode"
                                source="matnrCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.batch"
                                source="batch"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.splrBatch"
                                source="splrBatch"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.orderCode"
                                source="orderCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.orderItemId"
                                source="orderItemId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.unit"
                                source="unit"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.trackCode"
                                source="trackCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.fieldsIndex"
                                source="fieldsIndex"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.anfme"
                                source="anfme"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.workQty"
                                source="workQty"
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default WaveItemEdit;
rsf-admin/src/page/waveItem/WaveItemList.jsx
New file
@@ -0,0 +1,178 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    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,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveItemCreate from "./WaveItemCreate";
import WaveItemPanel from "./WaveItemPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="waveId" label="table.field.waveItem.waveId" />,
    <TextInput source="waveCode" label="table.field.waveItem.waveCode" />,
    <NumberInput source="matnrId" label="table.field.waveItem.matnrId" />,
    <TextInput source="matnrName" label="table.field.waveItem.matnrName" />,
    <TextInput source="matnrCode" label="table.field.waveItem.matnrCode" />,
    <TextInput source="batch" label="table.field.waveItem.batch" />,
    <TextInput source="splrBatch" label="table.field.waveItem.splrBatch" />,
    <TextInput source="orderCode" label="table.field.waveItem.orderCode" />,
    <NumberInput source="orderItemId" label="table.field.waveItem.orderItemId" />,
    <TextInput source="unit" label="table.field.waveItem.unit" />,
    <TextInput source="trackCode" label="table.field.waveItem.trackCode" />,
    <TextInput source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />,
    <NumberInput source="anfme" label="table.field.waveItem.anfme" />,
    <NumberInput source="workQty" label="table.field.waveItem.workQty" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
        resettable
    />,
]
const WaveItemList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.waveItem"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='waveItem' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='waveItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <WaveItemPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="waveId" label="table.field.waveItem.waveId" />
                    <TextField source="waveCode" label="table.field.waveItem.waveCode" />
                    <NumberField source="matnrId" label="table.field.waveItem.matnrId" />
                    <TextField source="matnrName" label="table.field.waveItem.matnrName" />
                    <TextField source="matnrCode" label="table.field.waveItem.matnrCode" />
                    <TextField source="batch" label="table.field.waveItem.batch" />
                    <TextField source="splrBatch" label="table.field.waveItem.splrBatch" />
                    <TextField source="orderCode" label="table.field.waveItem.orderCode" />
                    <NumberField source="orderItemId" label="table.field.waveItem.orderItemId" />
                    <TextField source="unit" label="table.field.waveItem.unit" />
                    <TextField source="trackCode" label="table.field.waveItem.trackCode" />
                    <TextField source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />
                    <NumberField source="anfme" label="table.field.waveItem.anfme" />
                    <NumberField source="workQty" label="table.field.waveItem.workQty" />
                    <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>
            </List>
            <WaveItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='WaveItem Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveItemList;
rsf-admin/src/page/waveItem/WaveItemPanel.jsx
New file
@@ -0,0 +1,135 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const WaveItemPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.waveItem.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.waveId"
                                property={record.waveId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.waveCode"
                                property={record.waveCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrId"
                                property={record.matnrId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrName"
                                property={record.matnrName}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrCode"
                                property={record.matnrCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.batch"
                                property={record.batch}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.splrBatch"
                                property={record.splrBatch}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.orderCode"
                                property={record.orderCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.orderItemId"
                                property={record.orderItemId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.unit"
                                property={record.unit}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.trackCode"
                                property={record.trackCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.fieldsIndex"
                                property={record.fieldsIndex}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.anfme"
                                property={record.anfme}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.workQty"
                                property={record.workQty}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default WaveItemPanel;
rsf-admin/src/page/waveItem/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import WaveItemList from "./WaveItemList";
import WaveItemEdit from "./WaveItemEdit";
export default {
    list: WaveItemList,
    edit: WaveItemEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
@@ -15,15 +15,15 @@
        generator.frontendPrefixPath = "rsf-admin/";
        generator.sqlOsType = SqlOsType.MYSQL;
        generator.url="192.168.4.24:3306/rsf";
        generator.url="127.0.0.1:3306/rsf";
        generator.username="root";
        generator.password="34821015";
//        generator.url="47.97.1.152:51433;databasename=jkasrs";
//        generator.username="sa";
//        generator.password="Zoneyung@zy56$";
        generator.table="man_delivery_item";
        generator.tableDesc="综合单据明细";
        generator.table="man_device_bind";
        generator.tableDesc="立体库站点绑定";
        generator.packagePath="com.vincent.rsf.server.manager";
        generator.build();
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java
New file
@@ -0,0 +1,110 @@
package com.vincent.rsf.server.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
import com.vincent.rsf.server.common.domain.KeyValVo;
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.server.manager.entity.DeviceBind;
import com.vincent.rsf.server.manager.service.DeviceBindService;
import com.vincent.rsf.server.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
public class DeviceBindController extends BaseController {
    @Autowired
    private DeviceBindService deviceBindService;
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<DeviceBind, BaseParam> pageParam = new PageParam<>(baseParam, DeviceBind.class);
        return R.ok().add(deviceBindService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(deviceBindService.list());
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping({"/deviceBind/many/{ids}", "/deviceBinds/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(deviceBindService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @GetMapping("/deviceBind/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(deviceBindService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:save')")
    @OperationLog("Create 立体库站点绑定")
    @PostMapping("/deviceBind/save")
    public R save(@RequestBody DeviceBind deviceBind) {
        deviceBind.setCreateBy(getLoginUserId());
        deviceBind.setCreateTime(new Date());
        deviceBind.setUpdateBy(getLoginUserId());
        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.save(deviceBind)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(deviceBind);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:update')")
    @OperationLog("Update 立体库站点绑定")
    @PostMapping("/deviceBind/update")
    public R update(@RequestBody DeviceBind deviceBind) {
        deviceBind.setUpdateBy(getLoginUserId());
        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.updateById(deviceBind)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(deviceBind);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:remove')")
    @OperationLog("Delete 立体库站点绑定")
    @PostMapping("/deviceBind/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!deviceBindService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<DeviceBind> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(DeviceBind::getId, condition);
        }
        deviceBindService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(deviceBindService.list(), DeviceBind.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -203,5 +203,16 @@
        return outStockService.genOutStock(ids);
    }
    @PreAuthorize("hasAuthority('manager:outStock:update')")
    @ApiOperation("出库单生成波次")
    @PostMapping("/outStock/generate/wave")
    public R generateWave(@RequestBody Map<String, Object> params) {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        List<Long> ids = (List<Long>) params.get("ids");
        return outStockService.generateWaves(ids);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java
New file
@@ -0,0 +1,108 @@
package com.vincent.rsf.server.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
import com.vincent.rsf.server.common.domain.KeyValVo;
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.server.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.WaveService;
import com.vincent.rsf.server.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
public class WaveController extends BaseController {
    @Autowired
    private WaveService waveService;
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<Wave, BaseParam> pageParam = new PageParam<>(baseParam, Wave.class);
        return R.ok().add(waveService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(waveService.list());
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping({"/wave/many/{ids}", "/waves/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(waveService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @GetMapping("/wave/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(waveService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:wave:save')")
    @OperationLog("Create 波次单据")
    @PostMapping("/wave/save")
    public R save(@RequestBody Wave wave) {
        wave.setCreateBy(getLoginUserId());
        wave.setUpdateBy(getLoginUserId());
        if (!waveService.save(wave)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(wave);
    }
    @PreAuthorize("hasAuthority('manager:wave:update')")
    @OperationLog("Update 波次单据")
    @PostMapping("/wave/update")
    public R update(@RequestBody Wave wave) {
        wave.setUpdateBy(getLoginUserId());
        wave.setUpdateTime(new Date());
        if (!waveService.updateById(wave)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(wave);
    }
    @PreAuthorize("hasAuthority('manager:wave:remove')")
    @OperationLog("Delete 波次单据")
    @PostMapping("/wave/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!waveService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<Wave> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(Wave::getId, condition);
        }
        waveService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(waveService.list(), Wave.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveItemController.java
New file
@@ -0,0 +1,110 @@
package com.vincent.rsf.server.manager.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.common.annotation.OperationLog;
import com.vincent.rsf.server.common.domain.BaseParam;
import com.vincent.rsf.server.common.domain.KeyValVo;
import com.vincent.rsf.server.common.domain.PageParam;
import com.vincent.rsf.server.manager.entity.WaveItem;
import com.vincent.rsf.server.manager.service.WaveItemService;
import com.vincent.rsf.server.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
public class WaveItemController extends BaseController {
    @Autowired
    private WaveItemService waveItemService;
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<WaveItem, BaseParam> pageParam = new PageParam<>(baseParam, WaveItem.class);
        return R.ok().add(waveItemService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(waveItemService.list());
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping({"/waveItem/many/{ids}", "/waveItems/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(waveItemService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @GetMapping("/waveItem/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(waveItemService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:save')")
    @OperationLog("Create 波次明细")
    @PostMapping("/waveItem/save")
    public R save(@RequestBody WaveItem waveItem) {
        waveItem.setCreateBy(getLoginUserId());
        waveItem.setCreateTime(new Date());
        waveItem.setUpdateBy(getLoginUserId());
        waveItem.setUpdateTime(new Date());
        if (!waveItemService.save(waveItem)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(waveItem);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:update')")
    @OperationLog("Update 波次明细")
    @PostMapping("/waveItem/update")
    public R update(@RequestBody WaveItem waveItem) {
        waveItem.setUpdateBy(getLoginUserId());
        waveItem.setUpdateTime(new Date());
        if (!waveItemService.updateById(waveItem)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(waveItem);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:remove')")
    @OperationLog("Delete 波次明细")
    @PostMapping("/waveItem/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!waveItemService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<WaveItem> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(WaveItem::getId, condition);
        }
        waveItemService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(waveItemService.list(), WaveItem.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/DeviceSiteParame.java
@@ -20,7 +20,7 @@
    private List<Long> typeIds;
    @ApiModelProperty("作业站点")
    private String sites;
    private String site;
    @ApiModelProperty("设备编号")
    private String deviceCode;
@@ -28,4 +28,7 @@
    @ApiModelProperty("设备作业站点")
    private String deviceSites;
    @ApiModelProperty("目标站点")
    private String target;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java
New file
@@ -0,0 +1,133 @@
package com.vincent.rsf.server.manager.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("man_device_bind")
public class DeviceBind implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @ApiModelProperty(value= "id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 当前排号
     */
    @ApiModelProperty(value= "当前排号")
    private Integer currentRow;
    /**
     * 起始排号
     */
    @ApiModelProperty(value= "起始排号")
    private Integer startRow;
    /**
     * 终止排号
     */
    @ApiModelProperty(value= "终止排号")
    private Integer endRow;
    /**
     * 设备数量
     */
    @ApiModelProperty(value= "设备数量")
    private Integer deviceQty;
    /**
     * 起始设备号
     */
    @ApiModelProperty(value= "起始设备号")
    private Integer startDeviceNo;
    /**
     * 终止设备号
     */
    @ApiModelProperty(value= "终止设备号")
    private Integer endDeviceNo;
    /**
     * 站点list
     */
    @ApiModelProperty(value= "站点list")
    private String staList;
    /**
     * 库区类型
     */
    @ApiModelProperty(value= "库区类型")
    private Integer typeId;
    /**
     * 物料相似开关
     */
    @ApiModelProperty(value= "物料相似开关")
    private String beSimilar;
    /**
     * 空板靠近开关
     */
    @ApiModelProperty(value= "空板靠近开关")
    private String emptySimilar;
    public DeviceBind() {}
    public DeviceBind(Integer currentRow,Integer startRow,Integer endRow,Integer deviceQty,Integer startDeviceNo,Integer endDeviceNo,String staList,Integer typeId,String beSimilar,String emptySimilar) {
        this.currentRow = currentRow;
        this.startRow = startRow;
        this.endRow = endRow;
        this.deviceQty = deviceQty;
        this.startDeviceNo = startDeviceNo;
        this.endDeviceNo = endDeviceNo;
        this.staList = staList;
        this.typeId = typeId;
        this.beSimilar = beSimilar;
        this.emptySimilar = emptySimilar;
    }
//    DeviceBind deviceBind = new DeviceBind(
//            null,    // 当前排号
//            null,    // 起始排号
//            null,    // 终止排号
//            null,    // 设备数量
//            null,    // 起始设备号
//            null,    // 终止设备号
//            null,    // 站点list
//            null,    // 库区类型
//            null,    // 物料相似开关
//            null    // 空板靠近开关
//    );
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceSite.java
@@ -64,8 +64,8 @@
    /**
     * wcs站点编号
     */
    @ApiModelProperty(value= "wcs站点编号")
    private String wcsCode;
    @ApiModelProperty(value= "目标位置")
    private String target;
    /**
     * 站点标签
@@ -154,7 +154,7 @@
        this.type = type;
        this.site = site;
        this.name = name;
        this.wcsCode = wcsCode;
        this.target = wcsCode;
        this.label = label;
        this.device = device;
        this.deviceCode = deviceCode;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Wave.java
New file
@@ -0,0 +1,249 @@
package com.vincent.rsf.server.manager.entity;
import java.text.SimpleDateFormat;
import java.util.Date;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("man_wave")
@Accessors(chain = true)
public class Wave implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 波次号
     */
    @ApiModelProperty(value= "波次号")
    private String code;
    /**
     * 波次类型 0: 手动  1: 自动
     */
    @ApiModelProperty(value= "波次类型 0: 手动  1: 自动  ")
    private Short type;
    /**
     * 波次状态 0: 初始化  1: 生成任务  2: 任务播种  3: 完成
     */
    @ApiModelProperty(value= "波次状态 0: 初始化  1: 生成任务  2: 任务播种  3: 完成  ")
    private Short exceStatus;
    /**
     * 单据数量
     */
    @ApiModelProperty(value= "单据数量")
    private Double anfme;
    /**
     * 已完成数量
     */
    @ApiModelProperty(value= "已完成数量")
    private Double qty;
    /**
     * 单据数量
     */
    @ApiModelProperty(value= "单据数量")
    private Integer orderNum;
    /**
     * 状态 1: 正常  0: 禁用
     */
    @ApiModelProperty(value= "状态 1: 正常  0: 禁用  ")
    private Integer status;
    /**
     * 所属机构
     */
    @ApiModelProperty(value= "所属机构")
    private Long tenantId;
    /**
     * 添加时间
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 添加人员
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * 修改时间
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 修改人员
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * 是否删除 1: 是  0: 否
     */
    @ApiModelProperty(value= "是否删除 1: 是  0: 否  ")
    @TableLogic
    private Integer deleted;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public Wave() {}
    public Wave(String code,Short type,Short exceStatus,Double anfme,Double qty,Integer orderNum,Integer status,Long tenantId,Date createTime,Long createBy,Date updateTime,Long updateBy,Integer deleted,String memo) {
        this.code = code;
        this.type = type;
        this.exceStatus = exceStatus;
        this.anfme = anfme;
        this.qty = qty;
        this.orderNum = orderNum;
        this.status = status;
        this.tenantId = tenantId;
        this.createTime = createTime;
        this.createBy = createBy;
        this.updateTime = updateTime;
        this.updateBy = updateBy;
        this.deleted = deleted;
        this.memo = memo;
    }
//    Wave wave = new Wave(
//            null,    // 波次号
//            null,    // 波次类型
//            null,    // 波次状态
//            null,    // 单据数量
//            null,    // 已完成数量
//            null,    // 单据数量
//            null,    // 状态
//            null,    // 所属机构
//            null,    // 添加时间
//            null,    // 添加人员
//            null,    // 修改时间
//            null,    // 修改人员
//            null,    // 是否删除
//            null    // 备注
//    );
    public String getType$(){
        if (null == this.type){ return null; }
        switch (this.type){
            case 0:
                return "手动";
            case 1:
                return "自动";
            default:
                return String.valueOf(this.type);
        }
    }
    public String getExceStatus$(){
        if (null == this.exceStatus){ return null; }
        switch (this.exceStatus){
            case 0:
                return "初始化";
            case 1:
                return "生成任务";
            case 2:
                return "任务播种";
            case 3:
                return "完成";
            default:
                return String.valueOf(this.exceStatus);
        }
    }
    public String getStatus$(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "禁用";
            default:
                return String.valueOf(this.status);
        }
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getCreateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public String getUpdateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java
New file
@@ -0,0 +1,291 @@
package com.vincent.rsf.server.manager.entity;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.text.SimpleDateFormat;
import java.util.Date;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@Accessors(chain = true)
@TableName("man_wave_item")
public class WaveItem implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 波次ID
     */
    @ApiModelProperty(value= "波次ID")
    private Long waveId;
    /**
     * 波次号
     */
    @ApiModelProperty(value= "波次号")
    private String waveCode;
    /**
     * 商品ID
     */
    @ApiModelProperty(value= "商品ID")
    private Long matnrId;
    /**
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String matnrName;
    /**
     * 商品编号
     */
    @ApiModelProperty(value= "商品编号")
    private String matnrCode;
    /**
     * 批号
     */
    @ApiModelProperty(value= "批号")
    private String batch;
    /**
     * 供应商批次
     */
    @ApiModelProperty(value= "供应商批次")
    private String splrBatch;
    /**
     * 单据编码
     */
    @ApiModelProperty(value= "单据编码")
    private String orderCode;
    /**
     * 单据ID
     */
    @ApiModelProperty(value= "单据ID")
    private Long orderItemId;
    @ApiModelProperty("订单ID")
    private Long orderId;
    /**
     * 单位
     */
    @ApiModelProperty(value= "单位")
    private String unit;
    /**
     * 跟踪码
     */
    @ApiModelProperty(value= "跟踪码")
    private String trackCode;
    /**
     * 商品库存索引
     */
    @ApiModelProperty(value= "商品库存索引")
    private String fieldsIndex;
    /**
     * 数量
     */
    @ApiModelProperty(value= "数量")
    private Double anfme;
    @ApiModelProperty("已完成数量")
    private Double qty;
    /**
     * 工作数量
     */
    @ApiModelProperty(value= "工作数量")
    private Double workQty;
    /**
     * 所属机构
     */
    @ApiModelProperty(value= "所属机构")
    private Long tenantId;
    /**
     * 状态 1: 正常  0: 禁用
     */
    @ApiModelProperty(value= "状态 1: 正常  0: 禁用  ")
    private Integer status;
    /**
     * 是否删除 1: 是  0: 否
     */
    @ApiModelProperty(value= "是否删除 1: 是  0: 否  ")
    @TableLogic
    private Integer deleted;
    /**
     * 添加时间
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 添加人员
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * 修改时间
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 修改人员
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public WaveItem() {}
    public WaveItem(Long waveId,String waveCode,Long matnrId,String matnrName,String matnrCode,String batch,String splrBatch,String orderCode,Long orderItemId,String unit,String trackCode,String fieldsIndex,Double anfme,Double workQty,Long tenantId,Integer status,Integer deleted,Date createTime,Long createBy,Date updateTime,Long updateBy,String memo) {
        this.waveId = waveId;
        this.waveCode = waveCode;
        this.matnrId = matnrId;
        this.matnrName = matnrName;
        this.matnrCode = matnrCode;
        this.batch = batch;
        this.splrBatch = splrBatch;
        this.orderCode = orderCode;
        this.orderItemId = orderItemId;
        this.unit = unit;
        this.trackCode = trackCode;
        this.fieldsIndex = fieldsIndex;
        this.anfme = anfme;
        this.workQty = workQty;
        this.tenantId = tenantId;
        this.status = status;
        this.deleted = deleted;
        this.createTime = createTime;
        this.createBy = createBy;
        this.updateTime = updateTime;
        this.updateBy = updateBy;
        this.memo = memo;
    }
//    WaveItem waveItem = new WaveItem(
//            null,    // 波次ID
//            null,    // 波次号
//            null,    // 商品ID
//            null,    // 物料名称
//            null,    // 商品编号
//            null,    // 批号
//            null,    // 供应商批次
//            null,    // 单据编码
//            null,    // 单据ID
//            null,    // 单位
//            null,    // 跟踪码
//            null,    // 商品库存索引
//            null,    // 数量
//            null,    // 工作数量
//            null,    // 所属机构
//            null,    // 状态
//            null,    // 是否删除
//            null,    // 添加时间
//            null,    // 添加人员
//            null,    // 修改时间
//            null,    // 修改人员
//            null    // 备注
//    );
    public String getStatus$(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "禁用";
            default:
                return String.valueOf(this.status);
        }
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getCreateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public String getUpdateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/WaveExceStatus.java
New file
@@ -0,0 +1,27 @@
package com.vincent.rsf.server.manager.enums;
/**
 * @author Ryan
 * @version 1.0
 * @title WaveExceStatus
 * @description
 * @create 2025/4/24 17:56
 */
public enum WaveExceStatus {
    //波次执行状态
    WAVE_EXCE_STATUS_INIT("0", "初始化"),
    WAVE_EXCE_STATUS_TASK("1", "生成任务"),
    WAVE_EXCE_STATUS_SPEED("2", "播种任务"),
    WAVE_EXCE_STATUS_DONE("3", "完成"),
    ;
    WaveExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
    }
    public Short val;
    public String desc;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/DeviceBindMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.DeviceBind;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface DeviceBindMapper extends BaseMapper<DeviceBind> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveItemMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.WaveItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface WaveItemMapper extends BaseMapper<WaveItem> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.Wave;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface WaveMapper extends BaseMapper<Wave> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/DeviceBindService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.DeviceBind;
public interface DeviceBindService extends IService<DeviceBind> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -14,4 +14,6 @@
    R cancelOutOrder(String id);
    R genOutStock(List<Long> ids);
    R generateWaves(List<Long> ids);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveItemService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.WaveItem;
public interface WaveItemService extends IService<WaveItem> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.Wave;
public interface WaveService extends IService<Wave> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceBindServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.DeviceBindMapper;
import com.vincent.rsf.server.manager.entity.DeviceBind;
import com.vincent.rsf.server.manager.service.DeviceBindService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("deviceBindService")
public class DeviceBindServiceImpl extends ServiceImpl<DeviceBindMapper, DeviceBind> implements DeviceBindService {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceSiteServiceImpl.java
@@ -37,25 +37,32 @@
        if (Objects.isNull(param.getDeviceSites()) || StringUtils.isBlank(param.getDeviceSites())) {
            throw new CoolException("初始化失败: 设备作业站点不能为空!!");
        }
        if (Objects.isNull(param.getSites()) || StringUtils.isBlank(param.getSites())) {
        if (Objects.isNull(param.getSite()) || StringUtils.isBlank(param.getSite())) {
            throw new CoolException("初始化失败: 作业站点不能为空!!");
        }
        if (Objects.isNull(param.getTypeIds()) || param.getTypeIds().isEmpty()) {
            throw new CoolException("初始化失败: 作业类型不能为空!!");
        }
        List<String> sites = Arrays.asList(StringUtils.split(param.getSites(), ","));
        if (Objects.isNull(param.getTarget()) || param.getTarget().isEmpty()) {
            throw new CoolException("初始化失败: 目标站点不能为空!!");
        }
        List<String> sites = Arrays.asList(StringUtils.split(param.getSite(), ","));
        List<String> dvSites = Arrays.asList(StringUtils.split(param.getDeviceSites(), ","));
        List<String> targets = Arrays.asList(StringUtils.split(param.getTarget(), ","));
        List<DeviceSite> deviceSites =  new ArrayList<>();
        for (String site : sites) {
            for (String deviceSite : dvSites) {
                for (Long id : param.getTypeIds()) {
                    DeviceSite site1 = new DeviceSite();
                    site1.setType(id + "")
                            .setSite(site)
                            .setDevice(param.getDeviceType())
                            .setDeviceSite(deviceSite)
                            .setDeviceCode(param.getDeviceCode());
                    deviceSites.add(site1);
                    for (String target : targets) {
                        DeviceSite site1 = new DeviceSite();
                        site1.setType(id + "")
                                .setSite(site)
                                .setDevice(param.getDeviceType())
                                .setDeviceSite(deviceSite)
                                .setTarget(target)
                                .setDeviceCode(param.getDeviceCode());
                        deviceSites.add(site1);
                    }
                }
            }
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -13,6 +13,7 @@
import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam;
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.enums.WaveExceStatus;
import com.vincent.rsf.server.manager.mapper.AsnOrderMapper;
import com.vincent.rsf.server.manager.mapper.PurchaseMapper;
import com.vincent.rsf.server.manager.service.*;
@@ -45,15 +46,16 @@
    private AsnOrderLogService asnOrderLogService;
    @Autowired
    private AsnOrderItemLogService asnOrderItemLogService;
    @Autowired
    private DeliveryItemService deliveryItemService;
    @Autowired
    private DeliveryService deliveryService;
    @Autowired
    private MatnrService matnrService;
    @Autowired
    private WaveService waveService;
    @Autowired
    private WaveItemService waveItemService;
    /**
@@ -157,6 +159,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R cancelOutOrder(String id) {
        //TODO 出库单取消流程,QMS(单据取消)->DO单->出库单->波次->判断是否全单据->全单据下发取消任务至WCS,非全单数据取消删除流程所有关联数据
        if (Cools.isEmpty(id)) {
            throw new CoolException("参数不能为空!!");
        }
@@ -238,4 +241,58 @@
        });
        return R.ok();
    }
    /**
     * @param
     * @return
     * @author Ryan
     * @description 生成波次
     * @time 2025/4/24 15:04
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R generateWaves(List<Long> ids) {
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        List<AsnOrder> orders = this.listByIds(ids);
        if (orders.isEmpty()) {
            throw new CoolException("单据不存在!!");
        }
        double sum = orders.stream().mapToDouble(AsnOrder::getAnfme).sum();
        Wave wave = new Wave();
        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_WAVE_TYPE, null);
        if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
            throw new CoolException("编码规则错误:请要查看「SYS_WAVE_TYPE」是否设置成功!!");
        }
        wave.setOrderNum(ids.size())
                .setType(Short.parseShort("1"))
                .setCode(ruleCode)
                .setExceStatus(WaveExceStatus.WAVE_EXCE_STATUS_INIT.val)
                .setAnfme(sum);
        if (!waveService.save(wave)) {
            throw new CoolException("波次保存失败!!");
        }
        List<Long> list = orders.stream().map(AsnOrder::getId).collect(Collectors.toList());
        List<WaveItem> items = new ArrayList<>();
        List<AsnOrderItem> orderItems = asnOrderItemService
                .list(new LambdaQueryWrapper<AsnOrderItem>()
                        .in(AsnOrderItem::getAsnId, list));
        for (AsnOrderItem item : orderItems) {
            WaveItem waveItem = new WaveItem();
            BeanUtils.copyProperties(item, waveItem);
            waveItem.setWaveId(wave.getId())
                    .setWaveCode(wave.getCode())
                    .setOrderId(item.getAsnId())
                    .setOrderCode(item.getAsnCode())
                    .setOrderItemId(item.getId());
            items.add(waveItem);
        }
        if (!waveItemService.saveBatch(items)) {
            throw new CoolException("波次明细保存失败!!");
        }
        return R.ok("操作完成!!");
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveItemServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.WaveItemMapper;
import com.vincent.rsf.server.manager.entity.WaveItem;
import com.vincent.rsf.server.manager.service.WaveItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("waveItemService")
public class WaveItemServiceImpl extends ServiceImpl<WaveItemMapper, WaveItem> implements WaveItemService {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.WaveMapper;
import com.vincent.rsf.server.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.WaveService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("waveService")
public class WaveServiceImpl extends ServiceImpl<WaveMapper, Wave> implements WaveService {
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
@@ -69,4 +69,9 @@
     */
    public final static String SYS_OUT_STOCK_CODE = "sys_out_stock_code";
    /**
     * 波次类型
     */
    public final static String SYS_WAVE_TYPE = "sys_wave_type";
}
rsf-server/src/main/java/deviceBind.sql
New file
@@ -0,0 +1,31 @@
-- save deviceBind record
-- mysql
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.deviceBind', '0', '/manager/deviceBind', 'deviceBind', '0' , '0', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 立体库站点绑定', '', '1', 'manager:deviceBind:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 立体库站点绑定', '', '1', 'manager:deviceBind:save', '1', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 立体库站点绑定', '', '1', 'manager:deviceBind:update', '2', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 立体库站点绑定', '', '1', 'manager:deviceBind:remove', '3', '1', '1');
-- locale menu name
deviceBind: 'DeviceBind',
-- locale field
deviceBind: {
    currentRow: "currentRow",
    startRow: "startRow",
    endRow: "endRow",
    deviceQty: "deviceQty",
    startDeviceNo: "startDeviceNo",
    endDeviceNo: "endDeviceNo",
    staList: "staList",
    typeId: "typeId",
    beSimilar: "beSimilar",
    emptySimilar: "emptySimilar",
},
-- ResourceContent
import deviceBind from './deviceBind';
case 'deviceBind':
    return deviceBind;
rsf-server/src/main/java/wave.sql
New file
@@ -0,0 +1,27 @@
-- save wave record
-- mysql
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.wave', '0', '/manager/wave', 'wave', '0' , '0', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 波次单据', '', '1', 'manager:wave:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 波次单据', '', '1', 'manager:wave:save', '1', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 波次单据', '', '1', 'manager:wave:update', '2', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 波次单据', '', '1', 'manager:wave:remove', '3', '1', '1');
-- locale menu name
wave: 'Wave',
-- locale field
wave: {
    code: "code",
    type: "type",
    exceStatus: "exceStatus",
    anfme: "anfme",
    qty: "qty",
    orderNum: "orderNum",
},
-- ResourceContent
import wave from './wave';
case 'wave':
    return wave;
rsf-server/src/main/java/waveItem.sql
New file
@@ -0,0 +1,35 @@
-- save waveItem record
-- mysql
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.waveItem', '0', '/manager/waveItem', 'waveItem', '0' , '0', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 波次明细', '', '1', 'manager:waveItem:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 波次明细', '', '1', 'manager:waveItem:save', '1', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 波次明细', '', '1', 'manager:waveItem:update', '2', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 波次明细', '', '1', 'manager:waveItem:remove', '3', '1', '1');
-- locale menu name
waveItem: 'WaveItem',
-- locale field
waveItem: {
    waveId: "waveId",
    waveCode: "waveCode",
    matnrId: "matnrId",
    matnrName: "matnrName",
    matnrCode: "matnrCode",
    batch: "batch",
    splrBatch: "splrBatch",
    orderCode: "orderCode",
    orderItemId: "orderItemId",
    unit: "unit",
    trackCode: "trackCode",
    fieldsIndex: "fieldsIndex",
    anfme: "anfme",
    workQty: "workQty",
},
-- ResourceContent
import waveItem from './waveItem';
case 'waveItem':
    return waveItem;
rsf-server/src/main/resources/mapper/manager/DeviceBindMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.DeviceBindMapper">
</mapper>
rsf-server/src/main/resources/mapper/manager/WaveItemMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.WaveItemMapper">
</mapper>
rsf-server/src/main/resources/mapper/manager/WaveMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.WaveMapper">
</mapper>