From 365b409cdb629396cd2cbb2635ee10309cd3cd96 Mon Sep 17 00:00:00 2001
From: wj <2932352213@qq.com>
Date: 星期三, 06 五月 2026 13:35:37 +0800
Subject: [PATCH] feat:添加批量放置货架功能,添加导出弹窗,可导出筛选的数据
---
zy-acs-manager/src/main/resources/application.yml | 6
zy-acs-flow/src/i18n/en.js | 18 +
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java | 48 ++++
zy-acs-flow/src/map/BatchShelfDialog.jsx | 133 +++++++++++++
zy-acs-flow/src/map/tool.js | 5
zy-acs-flow/src/i18n/zh.js | 17 +
zy-acs-flow/src/map/MapPage.jsx | 103 ++++++++++
zy-acs-flow/src/page/code/CodeList.jsx | 217 ++++++++++++--------
8 files changed, 452 insertions(+), 95 deletions(-)
diff --git a/zy-acs-flow/src/i18n/en.js b/zy-acs-flow/src/i18n/en.js
index ca33718..f2ed3c7 100644
--- a/zy-acs-flow/src/i18n/en.js
+++ b/zy-acs-flow/src/i18n/en.js
@@ -566,7 +566,11 @@
endTime: "end time",
errTime: "err time",
errDesc: "error",
+ batchshelving: "Batch Shelving",
+
+
},
+
mission: {
backpack: 'Backpack',
code: 'Code',
@@ -663,6 +667,7 @@
code: 'Send Code',
},
},
+
settings: {
resetPwd: {
currPwd: 'Current Password',
@@ -895,6 +900,17 @@
},
},
},
+ batch: {
+ title: 'Batch Shelving Settings',
+ startX: 'Start X (mm)',
+ startY: 'Start Y (mm)',
+ autoOffset: 'Auto Offset',
+ rows: 'Rows',
+ cols: 'Columns',
+ gapX: 'Row Spacing (mm)',
+ gapY: 'Column Spacing (mm)',
+ angle: 'Shelf Angle (deg)',
+ },
devices: {
title: 'Icons',
shelf: 'SHELF',
@@ -912,6 +928,7 @@
monitor: 'Console',
save: 'Save Map',
clear: 'Clear Map',
+ batchshelving: "Batch Shelving",
adapt: 'ADAPT',
rotate: 'ROTATE',
flip: 'FLIP',
@@ -1074,6 +1091,7 @@
},
},
}
+
};
export default customEnglishMessages;
diff --git a/zy-acs-flow/src/i18n/zh.js b/zy-acs-flow/src/i18n/zh.js
index 5342c7d..61b42a7 100644
--- a/zy-acs-flow/src/i18n/zh.js
+++ b/zy-acs-flow/src/i18n/zh.js
@@ -546,6 +546,7 @@
endTime: "缁撴潫鏃堕棿",
duration: "鎸佺画鏃堕暱",
state: "鐘舵��",
+
},
action: {
uuid: "缂栧彿",
@@ -566,6 +567,7 @@
endTime: "瀹屾垚鏃堕棿",
errTime: "寮傚父鏃堕棿",
errDesc: "寮傚父",
+ batchshelving: "鎵归噺涓婃灦",
},
mission: {
backpack: '鑳岀瘬',
@@ -663,6 +665,7 @@
code: '鑾峰彇楠岃瘉鐮�',
},
},
+
settings: {
resetPwd: {
currPwd: '褰撳墠瀵嗙爜',
@@ -895,6 +898,17 @@
},
},
},
+ batch: {
+ title: '璐ф灦闃靛垪璁剧疆',
+ startX: '璧风偣 X (姣背)',
+ startY: '璧风偣 Y (姣背)',
+ autoOffset: '鑷姩鍋忕Щ',
+ rows: '琛屾暟',
+ cols: '鍒楁暟',
+ gapX: '琛岄棿璺� (姣背)',
+ gapY: '鍒楅棿璺� (姣背)',
+ angle: '璐ф灦瑙掑害 (搴�)',
+ },
devices: {
title: '鍥炬爣搴�',
shelf: '璐ф灦',
@@ -912,6 +926,7 @@
monitor: '鏃ュ織鐩戞帶',
save: '淇濆瓨鍦板浘',
clear: '娓呯┖鍦板浘',
+ batchshelving: "鎵归噺涓婃灦",
adapt: '閫傞厤',
rotate: '鏃嬭浆',
flip: '缈昏浆',
@@ -931,6 +946,7 @@
addArea: '娣诲姞鍖哄煙',
cancelAddArea: '鍙栨秷娣诲姞',
areaList: '鍖哄煙鍒楄〃',
+
},
mode: {
observer: '瑙傚療妯″紡',
@@ -1074,6 +1090,7 @@
},
},
}
+
};
export default customChineseMessages;
diff --git a/zy-acs-flow/src/map/BatchShelfDialog.jsx b/zy-acs-flow/src/map/BatchShelfDialog.jsx
new file mode 100644
index 0000000..2d30d5b
--- /dev/null
+++ b/zy-acs-flow/src/map/BatchShelfDialog.jsx
@@ -0,0 +1,133 @@
+import React, { useState, useEffect } from 'react';
+import { Dialog, DialogTitle, DialogContent, DialogActions, TextField, Button, Stack } from '@mui/material';
+import { useTranslate } from 'react-admin';
+
+const BatchShelfDialog = ({ open, onClose, onConfirm, mapContainer }) => {
+ const translate = useTranslate();
+ const [startX, setStartX] = useState(0);
+ const [startY, setStartY] = useState(0);
+ const [rows, setRows] = useState(20);
+ const [cols, setCols] = useState(15);
+ const [gapX, setGapX] = useState(1000);
+ const [gapY, setGapY] = useState(1000);
+ const [angle, setAngle] = useState(0);
+
+ // 鑷姩鍋忕Щ锛氭壘鍒板凡鏈夎揣鏋剁殑鏈�澶� X 鍜� Y锛屽苟鍔犱笂瀵瑰簲闂磋窛浣滀负鏂拌捣鐐�
+ const handleAutoOffset = () => {
+ if (!mapContainer) {
+ console.warn('鍦板浘瀹瑰櫒鏈紶鍏ワ紝鏃犳硶鑷姩鍋忕Щ');
+ return;
+ }
+ let maxX = -Infinity;
+ let maxY = -Infinity;
+ mapContainer.children.forEach(child => {
+ if (child.data && (child.data.type === 'shelf' || child.data.type === 'SHELF')) {
+ maxX = Math.max(maxX, child.x);
+ maxY = Math.max(maxY, child.y);
+ }
+ });
+ setStartX(isFinite(maxX) ? maxX + gapX : 0);
+ setStartY(isFinite(maxY) ? maxY + gapY : 0);
+ };
+
+ const handleSubmit = () => {
+ onConfirm({
+ startX: parseFloat(startX),
+ startY: parseFloat(startY),
+ rows: parseInt(rows, 10),
+ cols: parseInt(cols, 10),
+ gapX: parseFloat(gapX),
+ gapY: parseFloat(gapY),
+ angle: parseFloat(angle),
+ });
+ };
+
+ // 杈呭姪鍑芥暟锛氬鐞嗘墜鍔ㄨ緭鍏ワ紝鍏佽绌哄�硷紙缃负0锛�
+ const handleNumberInput = (setter) => (e) => {
+ const val = e.target.value;
+ if (val === '') {
+ setter(0);
+ } else {
+ const num = parseFloat(val);
+ if (!isNaN(num)) setter(num);
+ }
+ };
+
+ return (
+ <Dialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
+ <DialogTitle>{translate('page.map.batch.title', { _: '璐ф灦闃靛垪璁剧疆' })}</DialogTitle>
+ <DialogContent>
+ <Stack spacing={2} sx={{ mt: 1 }}>
+ <Stack direction="row" spacing={1} alignItems="center">
+ <TextField
+ label={translate('page.map.batch.startX', { _: '璧风偣 X (姣背)' })}
+ type="text"
+ value={startX}
+ onChange={handleNumberInput(setStartX)}
+ fullWidth
+ />
+ <TextField
+ label={translate('page.map.batch.startY', { _: '璧风偣 Y (姣背)' })}
+ type="text"
+ value={startY}
+ onChange={handleNumberInput(setStartY)}
+ fullWidth
+ />
+ <Button variant="outlined" onClick={handleAutoOffset}>
+ {translate('page.map.batch.autoOffset', { _: '鑷姩鍋忕Щ' })}
+ </Button>
+ </Stack>
+
+ <TextField
+ label={translate('page.map.batch.rows', { _: '琛屾暟' })}
+ type="number"
+ value={rows}
+ onChange={e => setRows(parseInt(e.target.value, 10))}
+ inputProps={{ min: 1 }}
+ fullWidth
+ />
+ <TextField
+ label={translate('page.map.batch.cols', { _: '鍒楁暟' })}
+ type="number"
+ value={cols}
+ onChange={e => setCols(parseInt(e.target.value, 10))}
+ inputProps={{ min: 1 }}
+ fullWidth
+ />
+ <TextField
+ label={translate('page.map.batch.gapX', { _: '琛岄棿璺� (姣背)' })}
+ type="number"
+ value={gapX}
+ onChange={e => setGapX(parseFloat(e.target.value))}
+ inputProps={{ step: 100, min: 0 }}
+ fullWidth
+ />
+ <TextField
+ label={translate('page.map.batch.gapY', { _: '鍒楅棿璺� (姣背)' })}
+ type="number"
+ value={gapY}
+ onChange={e => setGapY(parseFloat(e.target.value))}
+ inputProps={{ step: 100, min: 0 }}
+ fullWidth
+ />
+ <TextField
+ label={translate('page.map.batch.angle', { _: '璐ф灦瑙掑害 (搴�)' })}
+ type="number"
+ value={angle}
+ onChange={e => setAngle(parseFloat(e.target.value))}
+ inputProps={{ step: 1 }}
+ fullWidth
+ />
+ </Stack>
+ </DialogContent>
+ <DialogActions>
+ <Button onClick={onClose}>{translate('ra.action.cancel')}</Button>
+ <Button onClick={handleSubmit} variant="contained" color="primary">
+ {translate('ra.action.confirm')}
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
+};
+
+export default BatchShelfDialog;
\ No newline at end of file
diff --git a/zy-acs-flow/src/map/MapPage.jsx b/zy-acs-flow/src/map/MapPage.jsx
index 552d4bd..3f7e37c 100644
--- a/zy-acs-flow/src/map/MapPage.jsx
+++ b/zy-acs-flow/src/map/MapPage.jsx
@@ -30,14 +30,22 @@
import AreaFab from "./header/AreaFab";
import MoreOperate from "./header/MoreOperate";
import NewsLogDialog from "./NewsLogDialog";
+import BatchShelfDialog from './BatchShelfDialog';
+import { DEVICE_TYPE } from './constants'; // 纭璺緞
let player;
let websocket;
const Map = () => {
+
const notify = useNotification();
const [sidebarOpen] = useSidebarState();
const translate = useTranslate();
+
+
+
+ // 璋冭瘯杈撳嚭
+ console.log('batchshelving translation:', translate('batchshelving'));
const theme = useTheme();
const themeMode = theme.palette.mode;
@@ -59,6 +67,80 @@
const [curSprite, setCurSprite] = useState(null);
const [batchSprites, setBatchSprites] = useState([]);
+
+ const [batchDialogOpen, setBatchDialogOpen] = useState(false);
+ const [history, setHistory] = useState([]);
+ const [historyIndex, setHistoryIndex] = useState(-1);
+ const startBatchPlace = () => {
+ setBatchDialogOpen(true);
+ };
+const recordAction = (action) => {
+ const newHistory = history.slice(0, historyIndex + 1);
+ newHistory.push(action);
+ setHistory(newHistory);
+ setHistoryIndex(newHistory.length - 1);
+};
+
+const undo = () => {
+ if (historyIndex < 0) return;
+ const action = history[historyIndex];
+ if (action.type === 'ADD_SHELVES') {
+ action.sprites.forEach(sprite => {
+ if (sprite.parent) mapContainer.removeChild(sprite);
+ sprite.destroy();
+ });
+ if (batchSprites.some(s => action.sprites.includes(s))) {
+ setBatchSprites([]);
+ setCurSprite(null);
+ }
+ }
+ setHistoryIndex(historyIndex - 1);
+};
+const handleBatchConfirm = async ({ startX, startY, rows, cols, gapX, gapY, angle }) => {
+ const start = { x: startX, y: startY };
+ const angleRad = angle * Math.PI / 180;
+ console.log(`浠� (${startX}, ${startY}) 鐢熸垚 ${rows} 琛� ${cols} 鍒楋紝闂磋窛(${gapX},${gapY})锛岃搴�${angle}掳`);
+
+ const existingPositions = new Set();
+ if (mapContainer) {
+ mapContainer.children.forEach(child => {
+ if (child.data?.type === DEVICE_TYPE.SHELF) {
+ const key = `${Math.round(child.x * 10)}_${Math.round(child.y * 10)}`;
+ existingPositions.add(key);
+ }
+ });
+ }
+
+ let created = 0;
+ const newSprites = [];
+ for (let i = 0; i < rows; i++) {
+ for (let j = 0; j < cols; j++) {
+ const posX = start.x + j * gapX;
+ const posY = start.y + i * gapY;
+ const key = `${Math.round(posX * 10)}_${Math.round(posY * 10)}`;
+ if (existingPositions.has(key)) continue;
+
+ const sprite = Tool.generateSprite(DEVICE_TYPE.SHELF);
+ if (!sprite) continue;
+ Tool.initSprite(sprite, DEVICE_TYPE.SHELF);
+ sprite.x = posX;
+ sprite.y = posY;
+ sprite.rotation = angleRad;
+ mapContainer.addChild(sprite);
+ Tool.beMovable(sprite);
+ created++;
+ newSprites.push(sprite);
+ }
+ }
+
+ if (newSprites.length) {
+ recordAction({ type: 'ADD_SHELVES', sprites: newSprites });
+ }
+
+ notify.success(`鎴愬姛鐢熸垚 ${created} 涓揣鏋讹紝璇疯寰椾繚瀛樺湴鍥綻);
+ setBatchDialogOpen(false);
+};
+
const [rcsStatus, setRcsStatus] = useState(null);
const [showRoutes, setShowRoutes] = useState(false);
@@ -375,6 +457,18 @@
Http.saveMapData(curZone);
}}
/>
+ <Button
+ variant="contained"
+ color="secondary"
+ onClick={startBatchPlace}
+ sx={{ textTransform: 'none' }}
+ >
+ {translate('batchshelving',{_: '鎵归噺璐ф灦'})}
+ </Button>
+
+ <Button onClick={undo} disabled={historyIndex < 0}>
+ {translate('ra.action.undo', { _: '鎾ら攢' })}
+ </Button>
</>
)}
@@ -602,12 +696,21 @@
open={logDialogOpen}
onClose={() => setLogDialogOpen(false)}
/>
+ <BatchShelfDialog
+ open={batchDialogOpen}
+ onClose={() => setBatchDialogOpen(false)}
+ onConfirm={handleBatchConfirm}
+ mapContainer={mapContainer}
+ />
</Box>
);
}
const MapPage = () => {
+
+
return (
+
<NotificationProvider>
<Map />
</NotificationProvider>
diff --git a/zy-acs-flow/src/map/tool.js b/zy-acs-flow/src/map/tool.js
index 2ffcb82..da7247e 100644
--- a/zy-acs-flow/src/map/tool.js
+++ b/zy-acs-flow/src/map/tool.js
@@ -90,9 +90,8 @@
let sprite;
switch (deviceType) {
case DEVICE_TYPE.SHELF:
- sprite = new PIXI.Sprite(PIXI.Texture.from(shelf, { resourceOptions: { scale: 1 } }));
- // sprite.width = 50;
- // sprite.height = 50;
+ sprite = new PIXI.Sprite(PIXI.Texture.from(shelf));
+ sprite.scale.set(2.0, 2.0); // 璋冩暣鍚堥�傜殑澶у皬
sprite.zIndex = DEVICE_Z_INDEX.SHELF;
break;
case DEVICE_TYPE.CHARGE:
diff --git a/zy-acs-flow/src/page/code/CodeList.jsx b/zy-acs-flow/src/page/code/CodeList.jsx
index 1f45bad..e1ca3d9 100644
--- a/zy-acs-flow/src/page/code/CodeList.jsx
+++ b/zy-acs-flow/src/page/code/CodeList.jsx
@@ -1,5 +1,4 @@
-import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
-import { useNavigate } from 'react-router-dom';
+import React, { useState } from "react";
import {
List,
DatagridConfigurable,
@@ -8,103 +7,170 @@
SelectColumnsButton,
EditButton,
FilterButton,
- CreateButton,
- ExportButton,
BulkDeleteButton,
WrapperField,
- useRecordContext,
useTranslate,
useListContext,
- useCreatePath,
TextField,
NumberField,
DateField,
BooleanField,
ReferenceField,
TextInput,
- DateTimeInput,
DateInput,
SelectInput,
NumberInput,
- ReferenceInput,
- ReferenceArrayInput,
- AutocompleteInput,
DeleteButton,
- FunctionField,
+ useNotify,
+ downloadCSV
} from 'react-admin';
-import { Box, Typography, Card, Stack } from '@mui/material';
+import { Box, Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { styled } from '@mui/material/styles';
import CodeCreate from "./CodeCreate";
import CodePanel from "./CodePanel";
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';
-import ImportButton from '../components/ImportButton'
+import ImportButton from '../components/ImportButton';
import { useCodeImport } from './useCodeImport';
-
-// import * as importTemp from './importTemp.csv?raw';
-// const IMPORT_TEMP_URL = `data:text/csv;name=crm_contacts_sample.csv;charset=utf-8,${encodeURIComponent(importTemp.default)}`;
+import PageDrawer from "../components/PageDrawer";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import { useLocation } from 'react-router-dom';
+import DownloadIcon from '@mui/icons-material/Download';
+// 鎴栬�� import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
const IMPORT_TEMP_URL = '/imports/code_import_template.xlsx';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
- '& .css-1vooibu-MuiSvgIcon-root': {
- height: '.9em'
- },
- '& .RaDatagrid-row': {
- cursor: 'auto'
- },
- '& .column-name': {
- },
- '& .opt': {
- width: 200
- },
+ '& .css-1vooibu-MuiSvgIcon-root': { height: '.9em' },
+ '& .RaDatagrid-row': { cursor: 'auto' },
+ '& .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="uuid" label="table.field.code.uuid" />,
<TextInput source="data" label="table.field.code.data" alwaysOn />,
<NumberInput source="x" label="table.field.code.x" />,
<NumberInput source="y" label="table.field.code.y" />,
- <SelectInput source="corner" label="table.field.code.corner"
- choices={[
- { id: '1', name: 'common.enums.true' },
- { id: '0', name: 'common.enums.false' },
- ]}
+ <SelectInput
+ source="corner"
+ label="table.field.code.corner"
+ choices={[{ id: '1', name: 'common.enums.true' }, { id: '0', name: 'common.enums.false' }]}
alwaysOn
/>,
- // <TextInput source="scale" label="table.field.code.scale" />,
- // <SelectInput source="spin" label="table.field.code.spin"
- // choices={[
- // { id: 0, name: 'page.code.enums.spin.na' },
- // { id: 1, name: 'page.code.enums.spin.cw' },
- // { id: 2, name: 'page.code.enums.spin.ccw' },
- // ]}
- // alwaysOn
- // emptyText={false}
- // />,
<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' },
- ]}
+ choices={[{ id: '1', name: 'common.enums.statusTrue' }, { id: '0', name: 'common.enums.statusFalse' }]}
/>,
-]
+];
-const CodeList = () => {
+// ===================== 鑷畾涔夊鍑烘寜閽� =====================
+const CustomExportButton = () => {
+ const [open, setOpen] = useState(false);
+ const notify = useNotify();
const translate = useTranslate();
+ // 鐩存帴浠庢祻瑙堝櫒鍦板潃鏍忚В鏋� filter 鍙傛暟
+ const getFilterFromUrl = () => {
+ const hash = window.location.hash; // 渚嬪 "#/code?filter=%7B%22corner%22%3A%220%22%7D"
+ const match = hash.match(/[?&]filter=([^&]+)/);
+ if (match) {
+ try {
+ const filterJson = decodeURIComponent(match[1]);
+ return JSON.parse(filterJson);
+ } catch (e) {
+ console.error('瑙f瀽 filter 澶辫触', e);
+ return {};
+ }
+ }
+ return {};
+ };
+
+ const handleExport = async (useCurrentFilter) => {
+ try {
+ let filter = {};
+ if (useCurrentFilter) {
+ filter = getFilterFromUrl();
+ }
+ console.log('鏈�缁堝鍑轰娇鐢ㄧ殑 filter:', filter);
+
+ const token = localStorage.getItem('Authorization');
+ // 灏� filter 杞崲涓烘煡璇㈠瓧绗︿覆锛屼緥濡� corner=0
+ const queryParams = new URLSearchParams(filter).toString();
+ const url = `/api/code/export${queryParams ? '?' + queryParams : ''}`;
+ console.log('璇锋眰 URL:', url);
+
+ const response = await fetch(url, {
+ method: 'POST', // 鏀瑰洖 POST锛堝洜涓轰箣鍓� POST 鑳借繑鍥� Excel锛�
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ // 灏濊瘯鍚屾椂灏� filter 浠� JSON 鏍煎紡鏀惧湪 body 涓紙鐪嬪悗绔槸鍚﹁鍙栵級
+ body: JSON.stringify(filter),
+ });
+
+ if (!response.ok) throw new Error(`瀵煎嚭澶辫触 ${response.status}`);
+
+ // 妫�鏌ュ搷搴旂被鍨嬶紝濡傛灉鏄� JSON 璇存槑鍚庣鎶ラ敊浜�
+ const contentType = response.headers.get('content-type');
+ if (contentType && contentType.includes('application/json')) {
+ const errorJson = await response.json();
+ console.error('鍚庣杩斿洖閿欒:', errorJson);
+ notify(`瀵煎嚭澶辫触: ${errorJson.msg || '鏈煡閿欒'}`, { type: 'error' });
+ return;
+ }
+
+ const blob = await response.blob();
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob);
+ link.download = `code_export_${new Date().toISOString().slice(0,19)}.xlsx`;
+ link.click();
+ URL.revokeObjectURL(link.href);
+ notify('瀵煎嚭鎴愬姛', { type: 'success' });
+ } catch (error) {
+ console.error(error);
+ notify('瀵煎嚭澶辫触', { type: 'error' });
+ }
+ setOpen(false);
+ };
+
+ return (
+ <>
+ <Button
+ color="primary"
+ onClick={() => setOpen(true)}
+ startIcon={<DownloadIcon />} // 娣诲姞鍚戜笅绠ご鍥炬爣
+ size="small" // 涓� ImportButton 淇濇寔涓�鑷寸殑灏忓昂瀵�
+ sx={{ textTransform: 'none' }} // 鍙�夛細姝e父澶у皬鍐欙紝鍖归厤瀵煎叆鎸夐挳
+ >
+ {translate('ra.action.export') || '瀵煎嚭'}
+ </Button>
+ <Dialog open={open} onClose={() => setOpen(false)}>
+ <DialogTitle>{translate('code.export.title',{_: '閫夋嫨瀵煎嚭鑼冨洿'})}</DialogTitle>
+ <DialogContent>{translate('code.export.message',{_: '璇烽�夋嫨瑕佸鍑虹殑鏁版嵁鑼冨洿'}) }</DialogContent>
+ <DialogActions>
+ <Button onClick={() => handleExport(true)}>
+ {translate('code.export.current',{_: '瀵煎嚭褰撳墠绛涢�夌粨鏋�'}) }
+ </Button>
+ <Button onClick={() => handleExport(false)}>
+ {translate('code.export.all',{_: '瀵煎嚭鍏ㄩ儴鏁版嵁' } )}
+ </Button>
+ <Button onClick={() => setOpen(false)}>
+ {translate('ra.action.cancel') || '鍙栨秷'}
+ </Button>
+ </DialogActions>
+ </Dialog>
+ </>
+ );
+};
+// =======================================================
+const CodeList = () => {
+ const translate = useTranslate();
const [createDialog, setCreateDialog] = useState(false);
const [drawerVal, setDrawerVal] = useState(false);
@@ -119,17 +185,17 @@
}),
marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
}}
- title={"menu.code"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+ title="menu.code"
+ empty={<EmptyData onClick={() => setCreateDialog(true)} />}
filters={filters}
sort={{ field: "id", order: "asc" }}
actions={(
<TopToolbar>
<FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
+ <MyCreateButton onClick={() => setCreateDialog(true)} />
<SelectColumnsButton preferenceKey='code' />
<ImportButton type="xlsx" importTemp={IMPORT_TEMP_URL} useImport={useCodeImport} onceBatch={10} />
- <MyExportButton />
+ <CustomExportButton /> {/* 鏇挎崲鍘熸湁鐨勫鍑烘寜閽� */}
</TopToolbar>
)}
perPage={DEFAULT_PAGE_SIZE}
@@ -148,23 +214,6 @@
<NumberField source="x" label="table.field.code.x" />
<NumberField source="y" label="table.field.code.y" />
<BooleanField source="cornerBool" label="table.field.code.corner" sortable={false} />
- {/* <TextField source="scale" label="table.field.code.scale" /> */}
- {/* <FunctionField
- label="table.field.code.spin"
- sortable={false}
- render={(record) => {
- switch (record.spin) {
- case 0:
- return translate('page.code.enums.spin.na');
- case 1:
- return translate('page.code.enums.spin.cw');
- case 2:
- return translate('page.code.enums.spin.ccw');
- default:
- return 'N/A';
- }
- }}
- /> */}
<ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
<TextField source="nickname" />
</ReferenceField>
@@ -181,18 +230,10 @@
</WrapperField>
</StyledDatagrid>
</List>
- <CodeCreate
- open={createDialog}
- setOpen={setCreateDialog}
- />
- <PageDrawer
- title='Code Detail'
- drawerVal={drawerVal}
- setDrawerVal={setDrawerVal}
- >
- </PageDrawer>
+ <CodeCreate open={createDialog} setOpen={setCreateDialog} />
+ <PageDrawer title='Code Detail' drawerVal={drawerVal} setDrawerVal={setDrawerVal} />
</Box>
- )
-}
+ );
+};
-export default CodeList;
+export default CodeList;
\ No newline at end of file
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java
index 4ccf9a6..d8ebe5e 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/CodeController.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zy.acs.common.utils.GsonUtils;
import com.zy.acs.common.utils.QrCodeCodecSupport;
@@ -202,7 +203,52 @@
@PreAuthorize("hasAuthority('manager:code:list')")
@PostMapping("/code/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(codeService.list(), Code.class), response);
+ // 1. 鎻愬彇绛涢�夋潯浠讹紙鍏煎鍓嶇 { filter: {...} } 鏍煎紡锛�
+ Map<String, Object> filter = map;
+ if (map != null && map.containsKey("filter")) {
+ Object filterObj = map.get("filter");
+ if (filterObj instanceof Map) {
+ filter = (Map<String, Object>) filterObj;
+ }
+ }
+
+ // 2. 鏋勫缓鏌ヨ鏉′欢锛圡yBatis-Plus锛�
+ QueryWrapper<Code> wrapper = new QueryWrapper<>();
+ if (filter != null && !filter.isEmpty()) {
+ // 鏍规嵁鍓嶇鍙兘浼犻�掔殑瀛楁娣诲姞鏉′欢锛堝瓧娈靛悕闇�涓庢暟鎹簱鍒楁垨瀹炰綋灞炴�у搴旓級
+ if (filter.containsKey("corner")) {
+ wrapper.eq("corner", filter.get("corner"));
+ }
+ if (filter.containsKey("condition")) {
+ String condition = (String) filter.get("condition");
+ wrapper.and(w -> w.like("data", condition).or().like("uuid", condition));
+ }
+ if (filter.containsKey("timeStart")) {
+ wrapper.ge("create_time", filter.get("timeStart"));
+ }
+ if (filter.containsKey("timeEnd")) {
+ wrapper.le("create_time", filter.get("timeEnd"));
+ }
+ if (filter.containsKey("x")) {
+ wrapper.eq("x", filter.get("x"));
+ }
+ if (filter.containsKey("y")) {
+ wrapper.eq("y", filter.get("y"));
+ }
+ if (filter.containsKey("memo")) {
+ wrapper.like("memo", filter.get("memo"));
+ }
+ if (filter.containsKey("status")) {
+ wrapper.eq("status", filter.get("status"));
+ }
+ // 杩樺彲浠ユ坊鍔犲叾浠栧瓧娈靛 data, uuid 绛�
+ }
+
+ // 3. 鏌ヨ绗﹀悎鏉′欢鐨勬暟鎹�
+ List<Code> list = codeService.list(wrapper);
+
+ // 4. 瀵煎嚭 Excel
+ ExcelUtil.build(ExcelUtil.create(list, Code.class), response);
}
@PreAuthorize("hasAuthority('manager:code:save')")
diff --git a/zy-acs-manager/src/main/resources/application.yml b/zy-acs-manager/src/main/resources/application.yml
index 1d2e04a..dc52dd7 100644
--- a/zy-acs-manager/src/main/resources/application.yml
+++ b/zy-acs-manager/src/main/resources/application.yml
@@ -7,9 +7,9 @@
static-path-pattern: /**
datasource:
driver-class-name: com.mysql.jdbc.Driver
- url: jdbc:mysql://localhost:3306/rcs_ctu_stable_hik?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+ url: jdbc:mysql://localhost:3306/rcs_ctu_stable_hik?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
- password: xltys1995
+ password: Mysql20260414
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
@@ -43,7 +43,7 @@
redis:
host: localhost
- password: xltys1995
+ password: Redis20260417
port: 6379
max: 30
min: 10
--
Gitblit v1.9.1