From 11e44b3012e829ff7c85363ce1a7c2c0457c72f0 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期二, 17 三月 2026 13:59:27 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/page/loc/LocList.jsx | 15 ++-
zy-acs-flow/src/page/loc/LocCreate.jsx | 5 +
zy-acs-flow/src/page/loc/LocEmptyData.jsx | 48 +++++++++
zy-acs-flow/src/page/loc/LocInit.jsx | 5 +
zy-acs-flow/src/page/loc/useLocImport.jsx | 17 +++
zy-acs-flow/public/imports/loc_import_template.xlsx | 0
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java | 183 ++++++++++++++++++++++++++++++++++++
7 files changed, 268 insertions(+), 5 deletions(-)
diff --git a/zy-acs-flow/public/imports/loc_import_template.xlsx b/zy-acs-flow/public/imports/loc_import_template.xlsx
index 5dae55f..d2f7fe9 100644
--- a/zy-acs-flow/public/imports/loc_import_template.xlsx
+++ b/zy-acs-flow/public/imports/loc_import_template.xlsx
Binary files differ
diff --git a/zy-acs-flow/src/page/loc/LocCreate.jsx b/zy-acs-flow/src/page/loc/LocCreate.jsx
index afebd46..cd6251c 100644
--- a/zy-acs-flow/src/page/loc/LocCreate.jsx
+++ b/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>
diff --git a/zy-acs-flow/src/page/loc/LocEmptyData.jsx b/zy-acs-flow/src/page/loc/LocEmptyData.jsx
new file mode 100644
index 0000000..ee621cc
--- /dev/null
+++ b/zy-acs-flow/src/page/loc/LocEmptyData.jsx
@@ -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;
diff --git a/zy-acs-flow/src/page/loc/LocInit.jsx b/zy-acs-flow/src/page/loc/LocInit.jsx
index 1ca4cb3..e1cad02 100644
--- a/zy-acs-flow/src/page/loc/LocInit.jsx
+++ b/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>
diff --git a/zy-acs-flow/src/page/loc/LocList.jsx b/zy-acs-flow/src/page/loc/LocList.jsx
index ecff579..8e791f4 100644
--- a/zy-acs-flow/src/page/loc/LocList.jsx
+++ b/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)}
diff --git a/zy-acs-flow/src/page/loc/useLocImport.jsx b/zy-acs-flow/src/page/loc/useLocImport.jsx
new file mode 100644
index 0000000..15e413e
--- /dev/null
+++ b/zy-acs-flow/src/page/loc/useLocImport.jsx
@@ -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,
+ };
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java
index bf5b8a7..01e3979 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/LocController.java
+++ b/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);
+ }
+
}
--
Gitblit v1.9.1