#
vincentlu
18 小时以前 11e44b3012e829ff7c85363ce1a7c2c0457c72f0
#
2个文件已添加
5个文件已修改
273 ■■■■■ 已修改文件
zy-acs-flow/public/imports/loc_import_template.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/loc/LocCreate.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/loc/LocEmptyData.jsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/loc/LocInit.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/loc/LocList.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/loc/useLocImport.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/imports/loc_import_template.xlsx
Binary files differ
zy-acs-flow/src/page/loc/LocCreate.jsx
@@ -31,6 +31,10 @@
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
import { compDirectChoices } from "./compDirect";
import ImportButton from '../components/ImportButton';
import { useLocImport } from './useLocImport';
const IMPORT_TEMP_URL = '/imports/loc_import_template.xlsx';
const LocCreate = (props) => {
    const { open, setOpen } = props;
@@ -219,6 +223,7 @@
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                                <SaveButton />
                                <ImportButton type="xlsx" importTemp={IMPORT_TEMP_URL} useImport={useLocImport} onceBatch={10} />
                            </Toolbar>
                        </DialogActions>
                    </Form>
zy-acs-flow/src/page/loc/LocEmptyData.jsx
New file
@@ -0,0 +1,48 @@
import React from 'react';
import { Box, Button, SvgIcon, Typography } from '@mui/material';
import { HotTub } from '@mui/icons-material';
import CorporateFareIcon from '@mui/icons-material/CorporateFare';
import { useTranslate } from 'react-admin';
const LocEmptyData = ({ onInit }) => {
    const translate = useTranslate();
    return (
        <Box
            display="flex"
            flexDirection="column"
            alignItems="center"
            justifyContent="flex-start"
            height="100vh"
            pt={10}
        >
            <SvgIcon component={HotTub} sx={{ fontSize: '18em', mb: 2, opacity: .5 }} />
            <Typography variant="h1" gutterBottom sx={{
                fontWeight: 'bold',
                fontSize: '2em',
                opacity: .5,
                mt: 2
            }}>
                {translate('create.empty.title')}
            </Typography>
            <Typography variant="subtitle1" gutterBottom sx={{
                fontSize: '1em',
                opacity: .5,
                mt: 2
            }}>
                {translate('create.empty.desc')}
            </Typography>
            <Button
                variant="contained"
                color="primary"
                startIcon={<CorporateFareIcon />}
                onClick={onInit}
                sx={{ fontSize: '1em', mt: 2 }}
            >
                {translate('page.loc.init')}
            </Button>
        </Box>
    );
};
export default LocEmptyData;
zy-acs-flow/src/page/loc/LocInit.jsx
@@ -29,6 +29,10 @@
import CheckIcon from '@mui/icons-material/Check';
import request from '@/utils/request'
import { compDirectChoices } from "./compDirect";
import ImportButton from '../components/ImportButton';
import { useLocImport } from './useLocImport';
const IMPORT_TEMP_URL = '/imports/loc_import_template.xlsx';
const LocInit = (props) => {
    const { open, setOpen } = props;
@@ -187,6 +191,7 @@
                    <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                        <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
                            <Button variant="contained" type="submit" startIcon={<CheckIcon />}>{translate('ra.action.confirm')}</Button>
                            <ImportButton type="xlsx" importTemp={IMPORT_TEMP_URL} useImport={useLocImport} onceBatch={10} />
                        </Toolbar>
                    </DialogActions>
                </Form>
zy-acs-flow/src/page/loc/LocList.jsx
@@ -38,7 +38,6 @@
import { styled } from '@mui/material/styles';
import LocCreate from "./LocCreate";
import LocPanel from "./LocPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
@@ -51,6 +50,11 @@
import BulkUpdateButton from "../components/BulkUpdateButton";
import LocBulkUpdateContent from './LocBulkUpdateContent';
import { getCompDirectLabel } from './compDirect';
import LocEmptyData from "./LocEmptyData";
import ImportButton from '../components/ImportButton';
import { useLocImport } from './useLocImport';
const IMPORT_TEMP_URL = '/imports/loc_import_template.xlsx';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -69,7 +73,7 @@
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <TextInput source="locNo" label="table.field.loc.locNo" alwaysOn />,
    <TextInput source="locNo" label="table.field.loc.locNo" />,
    <ReferenceInput source="locSts" label="table.field.loc.locSts" reference="locSts" alwaysOn>
        <AutocompleteInput label="table.field.loc.locSts" optionText="name" filterToQuery={(val) => ({ name: val })} />
    </ReferenceInput>,
@@ -135,7 +139,7 @@
                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.loc"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                empty={<LocEmptyData onInit={() => { setInitDialog(true); }} />}
                filters={filters}
                sort={{ field: "locNo", order: "asc" }}
                actions={(
@@ -146,6 +150,7 @@
                        }}><CorporateFareIcon /></Button>
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='loc' />
                        <ImportButton type="xlsx" importTemp={IMPORT_TEMP_URL} useImport={useLocImport} onceBatch={10} />
                        <MyExportButton />
                    </TopToolbar>
                )}
@@ -156,8 +161,8 @@
                    preferenceKey='loc'
                    bulkActionButtons={<LocBulkActionButtons />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <LocPanel />}
                    expandSingle={true}
                    expand={false}
                    // expandSingle={true}
                    omit={['id', 'locType', 'uuid', 'zpallet', 'barcode'
                        , 'statusBool', 'updateBy', 'createTime', 'createBy', 'memo']}
                    rowSx={rowSx(drawerVal || null)}
zy-acs-flow/src/page/loc/useLocImport.jsx
New file
@@ -0,0 +1,17 @@
import { useCallback } from 'react';
import request from '@/utils/request';
export function useLocImport() {
    const processBatch = useCallback(async (batch) => {
        const res = await request.post('/loc/import', batch);
        const { code, msg } = res.data;
        if (code !== 200) {
            console.error(msg);
            throw new Error(`Batch import failed: ${msg}`);
        }
    }, []);
    return {
        processBatch,
    };
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zy.acs.common.constant.CommonConstant;
import com.zy.acs.common.utils.Utils;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
@@ -12,9 +13,15 @@
import com.zy.acs.manager.common.domain.PageParam;
import com.zy.acs.manager.common.utils.ExcelUtil;
import com.zy.acs.manager.manager.controller.param.LocInitParam;
import com.zy.acs.manager.manager.entity.Code;
import com.zy.acs.manager.manager.entity.Loc;
import com.zy.acs.manager.manager.entity.LocSts;
import com.zy.acs.manager.manager.entity.LocType;
import com.zy.acs.manager.manager.entity.Zone;
import com.zy.acs.manager.manager.service.CodeService;
import com.zy.acs.manager.manager.service.LocService;
import com.zy.acs.manager.manager.service.LocStsService;
import com.zy.acs.manager.manager.service.LocTypeService;
import com.zy.acs.manager.manager.service.ZoneService;
import com.zy.acs.manager.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +40,12 @@
    private LocService locService;
    @Autowired
    private ZoneService zoneService;
    @Autowired
    private CodeService codeService;
    @Autowired
    private LocStsService locStsService;
    @Autowired
    private LocTypeService locTypeService;
    @PreAuthorize("hasAuthority('manager:loc:list')")
    @PostMapping("/loc/page")
@@ -133,6 +146,64 @@
    }
    @PreAuthorize("hasAuthority('manager:loc:save')")
    @PostMapping("/loc/import")
    public R importBatch(@RequestBody List<Map<String, Object>> rows) {
        if (Cools.isEmpty(rows)) {
            return R.ok();
        }
        Date now = new Date();
        Long userId = getLoginUserId();
        for (Map<String, Object> row : rows) {
            Zone zone = resolveZone(readCell(row, "zone"));
            Integer rowNo = parseInteger(row.get("row"), "row", true);
            Integer bayNo = parseInteger(row.get("bay"), "bay", true);
            Integer levNo = parseInteger(row.get("lev"), "lev", true);
            String locNo = buildLocNo(zone, rowNo, bayNo, levNo);
            Long locStsId = resolveLocStsId(readCell(row, "loc_sts"));
            Long locTypeId = resolveLocTypeId(readCell(row, "loc_type"));
            Long codeId = resolveCodeId(readCell(row, "code"));
            Integer compDirect = parseInteger(row.get("comp_direct"), "comp_direct", false);
            Double offset = parseDouble(row.get("offset"), "offset");
            Loc loc = locService.selectByLocNo(locNo);
            boolean exists = loc != null;
            if (!exists) {
                loc = new Loc();
                loc.setUuid(locNo);
                loc.setLocNo(locNo);
                loc.setName(locNo);
                loc.setStatus(1);
                loc.setDeleted(0);
                loc.setCreateBy(userId);
                loc.setCreateTime(now);
            }
            loc.setZoneId(zone.getId());
            loc.setRow(rowNo);
            loc.setBay(bayNo);
            loc.setLev(levNo);
            loc.setLocSts(locStsId);
            loc.setLocType(locTypeId);
            loc.setCode(codeId);
            loc.setCompDirect(compDirect);
            loc.setOffset(offset);
            loc.setUpdateBy(userId);
            loc.setUpdateTime(now);
            if (!exists) {
                if (!locService.save(loc)) {
                    throw new CoolException(locNo + " save fail !");
                }
            } else {
                if (!locService.updateById(loc)) {
                    throw new CoolException(locNo + " update fail !");
                }
            }
        }
        return R.ok();
    }
    @PreAuthorize("hasAuthority('manager:loc:save')")
    @OperationLog
    @PostMapping("/loc/init")
    public R init(@RequestBody LocInitParam param) {
@@ -186,4 +257,116 @@
        return R.ok("initialize success");
    }
    private Zone resolveZone(String identifier) {
        if (Cools.isEmpty(identifier)) {
            throw new CoolException("zone is required");
        }
        Zone zone = zoneService.getOne(new LambdaQueryWrapper<Zone>().eq(Zone::getUuid, identifier), false);
        if (zone == null) {
            zone = zoneService.getOne(new LambdaQueryWrapper<Zone>().eq(Zone::getName, identifier), false);
        }
        if (zone == null) {
            throw new CoolException("zone " + identifier + " not found");
        }
        return zone;
    }
    private Long resolveLocStsId(String identifier) {
        if (Cools.isEmpty(identifier)) {
            throw new CoolException("loc_sts is required");
        }
        LocSts locSts = locStsService.getOne(new LambdaQueryWrapper<LocSts>().eq(LocSts::getUuid, identifier), false);
        if (locSts == null) {
            locSts = locStsService.getOne(new LambdaQueryWrapper<LocSts>().eq(LocSts::getName, identifier), false);
        }
        if (locSts == null) {
            throw new CoolException("loc_sts " + identifier + " not found");
        }
        return locSts.getId();
    }
    private Long resolveLocTypeId(String identifier) {
        if (Cools.isEmpty(identifier)) {
            return null;
        }
        LocType locType = locTypeService.getOne(new LambdaQueryWrapper<LocType>().eq(LocType::getUuid, identifier), false);
        if (locType == null) {
            locType = locTypeService.getOne(new LambdaQueryWrapper<LocType>().eq(LocType::getName, identifier), false);
        }
        if (locType == null) {
            throw new CoolException("loc_type " + identifier + " not found");
        }
        return locType.getId();
    }
    private Long resolveCodeId(String data) {
        if (Cools.isEmpty(data)) {
            return null;
        }
        String codeData = Utils.zeroFill(data, CommonConstant.QR_CODE_LEN);
        Code code = codeService.getCacheByData(codeData);
        if (code == null) {
            throw new CoolException("code " + data + " not exist");
        }
        return code.getId();
    }
    private Integer parseInteger(Object value, String field, boolean required) {
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        String text = value == null ? null : String.valueOf(value).trim();
        if (Cools.isEmpty(text)) {
            if (required) {
                throw new CoolException(field + " is required");
            }
            return null;
        }
        try {
            return Double.valueOf(text).intValue();
        } catch (NumberFormatException ex) {
            throw new CoolException(field + " format error: " + text);
        }
    }
    private Double parseDouble(Object value, String field) {
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        String text = value == null ? null : String.valueOf(value).trim();
        if (Cools.isEmpty(text)) {
            return null;
        }
        try {
            return Double.valueOf(text);
        } catch (NumberFormatException ex) {
            throw new CoolException(field + " format error: " + text);
        }
    }
    private String readCell(Map<String, Object> row, String key) {
        if (row == null) {
            return null;
        }
        Object value = row.get(key);
        if (value == null) {
            return null;
        }
        String text = String.valueOf(value).trim();
        return Cools.isEmpty(text) ? null : text;
    }
    private String buildLocNo(Zone zone, Integer row, Integer bay, Integer lev) {
        if (zone == null || row == null || bay == null || lev == null) {
            throw new CoolException("zone/row/bay/lev cannot be null");
        }
        if (Cools.isEmpty(zone.getUuid())) {
            throw new CoolException("zone uuid is empty");
        }
        return Utils.zeroFill(zone.getUuid(), 2)
                + String.format("%03d", row)
                + String.format("%03d", bay)
                + String.format("%02d", lev);
    }
}