From 44ac1c9a2a6ee6ac9f618f4a63510f8f94d1b1a9 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期三, 25 三月 2026 15:43:51 +0800
Subject: [PATCH] #打印+导出
---
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx | 313 +++++-----
rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java | 216 +++++++
rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportService.java | 154 +++++
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasItemController.java | 72 ++
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasController.java | 50 +
rsf-admin/src/page/components/ListExportPrintButton.jsx | 545 +++++++++++++++++++
rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportHandler.java | 20
rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx | 216 ++++---
rsf-admin/src/page/components/listExportPrintUtils.js | 62 ++
9 files changed, 1,402 insertions(+), 246 deletions(-)
diff --git a/rsf-admin/src/page/components/ListExportPrintButton.jsx b/rsf-admin/src/page/components/ListExportPrintButton.jsx
new file mode 100644
index 0000000..8b7d770
--- /dev/null
+++ b/rsf-admin/src/page/components/ListExportPrintButton.jsx
@@ -0,0 +1,545 @@
+import React, { useMemo, useRef, useState } from "react";
+import DownloadIcon from "@mui/icons-material/GetApp";
+import PrintOutlinedIcon from "@mui/icons-material/PrintOutlined";
+import SaveIcon from '@mui/icons-material/Save';
+import {
+ Box,
+ Button as MuiButton,
+ CircularProgress,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Typography,
+} from '@mui/material';
+import {
+ Button,
+ useDataProvider,
+ useListContext,
+ useNotify,
+ useStore,
+ useTranslate,
+ useUnselectAll,
+} from "react-admin";
+import { useReactToPrint } from "react-to-print";
+import DialogCloseButton from "./DialogCloseButton";
+import { getValueBySource, resolveVisibleColumns } from "./listExportPrintUtils";
+
+const PREVIEW_ROW_LIMIT = 200;
+
+const formatColumnLabel = (label, translate) => {
+ if (!label) {
+ return '';
+ }
+
+ if (typeof label !== 'string') {
+ return String(label);
+ }
+
+ if (label.includes('.')) {
+ return translate(label, { _: label });
+ }
+
+ return label;
+};
+
+const formatValue = (value, fieldType) => {
+ if (value == null) {
+ return '';
+ }
+
+ if (fieldType === 'boolean') {
+ return value ? 'true' : 'false';
+ }
+
+ return String(value);
+};
+
+const padNumber = (value) => String(value).padStart(2, '0');
+
+const formatDate = (date = new Date()) => (
+ `${date.getFullYear()}骞�${padNumber(date.getMonth() + 1)}鏈�${padNumber(date.getDate())}鏃
+);
+
+const formatDateTime = (date = new Date()) => (
+ `${date.getFullYear()}/${padNumber(date.getMonth() + 1)}/${padNumber(date.getDate())} ${padNumber(date.getHours())}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}`
+);
+
+const getPrintLayoutConfig = (columnCount) => {
+ if (columnCount >= 16) {
+ return {
+ bodyFontSize: 8,
+ headerFontSize: 8,
+ cellPaddingX: 0.35,
+ cellPaddingY: 0.4,
+ sequenceWidth: 36,
+ };
+ }
+
+ if (columnCount >= 12) {
+ return {
+ bodyFontSize: 9,
+ headerFontSize: 9,
+ cellPaddingX: 0.45,
+ cellPaddingY: 0.5,
+ sequenceWidth: 42,
+ };
+ }
+
+ return {
+ bodyFontSize: 10,
+ headerFontSize: 10,
+ cellPaddingX: 0.6,
+ cellPaddingY: 0.65,
+ sequenceWidth: 52,
+ };
+};
+
+const getPrintOperator = () => {
+ try {
+ const user = JSON.parse(localStorage.getItem('user'));
+ return user?.fullName || user?.username || 'SYSTEM';
+ } catch (error) {
+ return 'SYSTEM';
+ }
+};
+
+const buildReportMeta = (reportTitle, count = 0) => ({
+ reportTitle,
+ reportDate: formatDate(),
+ printedAt: formatDateTime(),
+ operator: getPrintOperator(),
+ count,
+});
+
+const getRecordKey = (record, index) => (
+ record?.id ?? record?.barcode ?? record?.code ?? `row-${index}`
+);
+
+const PrintReportContent = ({
+ contentRef,
+ rows,
+ reportTitle,
+ printMeta,
+ translatedColumns,
+ printLayoutConfig,
+ sequenceColumnWidth,
+ dataColumnWidth,
+ previewNotice,
+}) => (
+ <Box
+ ref={contentRef}
+ sx={{
+ width: '277mm',
+ minHeight: '186mm',
+ mx: 'auto',
+ px: 1.5,
+ py: 2,
+ backgroundColor: '#fff',
+ color: '#111827',
+ boxSizing: 'border-box',
+ }}
+ >
+ <Typography
+ variant="h5"
+ sx={{
+ mb: 1,
+ textAlign: 'center',
+ fontWeight: 700,
+ letterSpacing: '0.08em',
+ }}
+ >
+ {reportTitle}
+ </Typography>
+ <Box
+ sx={{
+ mb: 1.5,
+ borderBottom: '2px solid #111827',
+ }}
+ />
+ <Box
+ sx={{
+ display: 'flex',
+ justifyContent: 'space-between',
+ gap: 2,
+ flexWrap: 'wrap',
+ mb: 2,
+ fontSize: 12,
+ color: '#374151',
+ }}
+ >
+ <Typography variant="body2">鎶ヨ〃鏃ユ湡: {printMeta.reportDate}</Typography>
+ <Typography variant="body2">鎵撳嵃浜�: {printMeta.operator}</Typography>
+ <Typography variant="body2">鎵撳嵃鏃堕棿: {printMeta.printedAt}</Typography>
+ <Typography variant="body2">璁板綍鏁�: {printMeta.count}</Typography>
+ </Box>
+ {previewNotice && (
+ <Typography variant="body2" sx={{ mb: 1.5, color: '#6b7280' }}>
+ {previewNotice}
+ </Typography>
+ )}
+ <TableContainer
+ component={Paper}
+ sx={{
+ boxShadow: 'none',
+ border: 'none',
+ backgroundColor: 'transparent',
+ }}
+ >
+ <Table
+ size="small"
+ sx={{
+ width: '100%',
+ tableLayout: 'fixed',
+ '& .MuiTableCell-root': {
+ borderBottom: 'none',
+ fontSize: printLayoutConfig.bodyFontSize,
+ px: printLayoutConfig.cellPaddingX,
+ py: printLayoutConfig.cellPaddingY,
+ whiteSpace: 'normal',
+ wordBreak: 'break-all',
+ overflowWrap: 'anywhere',
+ lineHeight: 1.35,
+ verticalAlign: 'top',
+ boxSizing: 'border-box',
+ },
+ '& .MuiTableHead-root .MuiTableCell-root': {
+ backgroundColor: '#f9fafb',
+ fontWeight: 700,
+ fontSize: printLayoutConfig.headerFontSize,
+ textAlign: 'center',
+ },
+ '& .MuiTableRow-root': {
+ pageBreakInside: 'avoid',
+ breakInside: 'avoid',
+ },
+ }}
+ >
+ <colgroup>
+ <col style={{ width: sequenceColumnWidth }} />
+ {translatedColumns.map((column) => (
+ <col key={column.source} style={{ width: dataColumnWidth }} />
+ ))}
+ </colgroup>
+ <TableHead>
+ <TableRow>
+ <TableCell align="center">
+ 搴忓彿
+ </TableCell>
+ {translatedColumns.map((column) => (
+ <TableCell key={column.source}>
+ {column.labelText}
+ </TableCell>
+ ))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {rows.map((record, index) => (
+ <TableRow key={getRecordKey(record, index)}>
+ <TableCell align="center">
+ {padNumber(index + 1)}
+ </TableCell>
+ {translatedColumns.map((column) => (
+ <TableCell key={column.source}>
+ {formatValue(getValueBySource(record, column.source), column.fieldType)}
+ </TableCell>
+ ))}
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ </Box>
+);
+
+const ListExportPrintButton = ({
+ storeKey,
+ columns = [],
+ title,
+ fileName,
+ maxResults = 1000,
+}) => {
+ const {
+ filter,
+ selectedIds = [],
+ filterValues = {},
+ resource,
+ sort,
+ total = 0,
+ } = useListContext();
+ const [hiddenColumns] = useStore(
+ storeKey,
+ columns.filter((column) => column.hidden).map((column) => column.source)
+ );
+ const [columnRanks] = useStore(`${storeKey}_columnRanks`, []);
+ const dataProvider = useDataProvider();
+ const notify = useNotify();
+ const translate = useTranslate();
+ const unselectAll = useUnselectAll(resource);
+ const previewContentRef = useRef(null);
+ const fullPrintContentRef = useRef(null);
+ const [printOpen, setPrintOpen] = useState(false);
+ const [printLoading, setPrintLoading] = useState(false);
+ const [printRows, setPrintRows] = useState([]);
+ const [renderFullPrintContent, setRenderFullPrintContent] = useState(false);
+
+ const defaultHiddenColumns = useMemo(
+ () => columns.filter((column) => column.hidden).map((column) => column.source),
+ [columns]
+ );
+
+ const visibleColumns = useMemo(
+ () => resolveVisibleColumns(storeKey, defaultHiddenColumns, columns, hiddenColumns, columnRanks),
+ [storeKey, defaultHiddenColumns, columns, hiddenColumns, columnRanks]
+ );
+
+ const mergedFilter = useMemo(
+ () => (filter ? { ...filterValues, ...filter } : filterValues),
+ [filter, filterValues]
+ );
+
+ const translatedTitle = useMemo(
+ () => formatColumnLabel(title || resource, translate),
+ [title, resource, translate]
+ );
+
+ const reportTitle = useMemo(() => `${translatedTitle}鎶ヨ〃`, [translatedTitle]);
+ const [printMeta, setPrintMeta] = useState(() => buildReportMeta(reportTitle, 0));
+
+ const translatedColumns = useMemo(
+ () => visibleColumns.map((column) => ({
+ ...column,
+ labelText: formatColumnLabel(column.label, translate),
+ })),
+ [visibleColumns, translate]
+ );
+ const previewRows = useMemo(
+ () => printRows.slice(0, PREVIEW_ROW_LIMIT),
+ [printRows]
+ );
+ const previewNotice = printRows.length > PREVIEW_ROW_LIMIT
+ ? `褰撳墠浠呴瑙堝墠 ${PREVIEW_ROW_LIMIT} 鏉★紝鎵撳嵃灏嗗寘鍚叏閮� ${printRows.length} 鏉¤褰昤
+ : '';
+ const printLayoutConfig = useMemo(
+ () => getPrintLayoutConfig(translatedColumns.length),
+ [translatedColumns.length]
+ );
+ const sequenceColumnWidth = `${printLayoutConfig.sequenceWidth}px`;
+ const dataColumnWidth = translatedColumns.length > 0
+ ? `calc((100% - ${sequenceColumnWidth}) / ${translatedColumns.length})`
+ : 'auto';
+
+ const isDisabled = total === 0 || translatedColumns.length === 0;
+
+ const getPerPage = () => {
+ if (selectedIds.length > 0) {
+ return Math.min(selectedIds.length, maxResults);
+ }
+
+ if (total > 0) {
+ return Math.min(total, maxResults);
+ }
+
+ return maxResults;
+ };
+
+ const downloadBlob = (response) => {
+ const url = window.URL.createObjectURL(
+ new Blob([response.data], { type: response.headers["content-type"] }),
+ );
+ const link = document.createElement("a");
+ link.href = url;
+ link.setAttribute("download", `${fileName || resource}.xlsx`);
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ window.URL.revokeObjectURL(url);
+ };
+
+ const handleExport = (event) => {
+ if (translatedColumns.length === 0) {
+ notify('鏆傛棤鍙鍑虹殑鍒�', { type: 'warning' });
+ return;
+ }
+
+ dataProvider
+ .export(resource, {
+ sort,
+ ids: selectedIds,
+ filter: mergedFilter,
+ pagination: { page: 1, perPage: getPerPage() },
+ meta: {
+ ...buildReportMeta(reportTitle, selectedIds.length > 0 ? selectedIds.length : total),
+ columns: translatedColumns.map((column) => ({
+ source: column.source,
+ label: column.labelText,
+ })),
+ },
+ })
+ .then((response) => {
+ downloadBlob(response);
+ unselectAll();
+ })
+ .catch((error) => {
+ console.error(error);
+ notify("ra.notification.http_error", { type: "error" });
+ });
+
+ if (typeof event?.stopPropagation === 'function') {
+ event.stopPropagation();
+ }
+ };
+
+ const loadPrintRows = async () => {
+ if (translatedColumns.length === 0) {
+ notify('鏆傛棤鍙墦鍗扮殑鍒�', { type: 'warning' });
+ return;
+ }
+
+ setPrintOpen(true);
+ setPrintLoading(true);
+ try {
+ let rows = [];
+ if (selectedIds.length > 0) {
+ const response = await dataProvider.getMany(resource, { ids: selectedIds });
+ rows = response.data || [];
+ } else {
+ const response = await dataProvider.getList(resource, {
+ sort,
+ filter: mergedFilter,
+ pagination: { page: 1, perPage: getPerPage() },
+ });
+ rows = response.data || [];
+ }
+ setPrintRows(rows);
+ setPrintMeta(buildReportMeta(reportTitle, rows.length));
+ } catch (error) {
+ console.error(error);
+ notify("ra.notification.http_error", { type: "error" });
+ setPrintOpen(false);
+ } finally {
+ setPrintLoading(false);
+ }
+ };
+
+ const handlePrintDialogClose = () => {
+ setPrintOpen(false);
+ setPrintRows([]);
+ };
+
+ const triggerPrint = useReactToPrint({
+ content: () => fullPrintContentRef.current,
+ documentTitle: translatedTitle,
+ onAfterPrint: () => {
+ setRenderFullPrintContent(false);
+ },
+ pageStyle: `
+ @page {
+ size: A4 landscape;
+ margin: 12mm 10mm;
+ }
+ html, body {
+ width: 297mm;
+ min-height: 210mm;
+ }
+ body {
+ -webkit-print-color-adjust: exact;
+ print-color-adjust: exact;
+ margin: 0;
+ }
+ `,
+ });
+
+ const handlePrint = async () => {
+ if (printLoading || printRows.length === 0) {
+ return;
+ }
+
+ setRenderFullPrintContent(true);
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ if (typeof triggerPrint === 'function') {
+ triggerPrint();
+ } else {
+ setRenderFullPrintContent(false);
+ }
+ };
+
+ return (
+ <>
+ <Button
+ onClick={handleExport}
+ label="ra.action.export"
+ disabled={isDisabled}
+ >
+ <DownloadIcon />
+ </Button>
+ <Button
+ onClick={loadPrintRows}
+ label="toolbar.print"
+ disabled={isDisabled}
+ >
+ <PrintOutlinedIcon />
+ </Button>
+ <Dialog open={printOpen} maxWidth="xl" fullWidth onClose={handlePrintDialogClose}>
+ <DialogCloseButton onClose={handlePrintDialogClose} />
+ <DialogTitle>{translatedTitle}</DialogTitle>
+ <DialogContent dividers>
+ {printLoading ? (
+ <Box sx={{ py: 8, display: 'flex', justifyContent: 'center' }}>
+ <CircularProgress size={28} />
+ </Box>
+ ) : printRows.length === 0 ? (
+ <Box sx={{ py: 8, textAlign: 'center' }}>
+ <Typography color="text.secondary">鏆傛棤鍙墦鍗版暟鎹�</Typography>
+ </Box>
+ ) : (
+ <PrintReportContent
+ contentRef={previewContentRef}
+ rows={previewRows}
+ reportTitle={reportTitle}
+ printMeta={printMeta}
+ translatedColumns={translatedColumns}
+ printLayoutConfig={printLayoutConfig}
+ sequenceColumnWidth={sequenceColumnWidth}
+ dataColumnWidth={dataColumnWidth}
+ previewNotice={previewNotice}
+ />
+ )}
+ </DialogContent>
+ <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper' }}>
+ <MuiButton
+ onClick={handlePrint}
+ disabled={printLoading || printRows.length === 0}
+ variant="contained"
+ startIcon={<SaveIcon />}
+ >
+ {translate('toolbar.confirm')}
+ </MuiButton>
+ </DialogActions>
+ </Dialog>
+ {renderFullPrintContent && (
+ <Box sx={{ position: 'fixed', left: '-10000px', top: 0, zIndex: -1 }}>
+ <PrintReportContent
+ contentRef={fullPrintContentRef}
+ rows={printRows}
+ reportTitle={reportTitle}
+ printMeta={printMeta}
+ translatedColumns={translatedColumns}
+ printLayoutConfig={printLayoutConfig}
+ sequenceColumnWidth={sequenceColumnWidth}
+ dataColumnWidth={dataColumnWidth}
+ />
+ </Box>
+ )}
+ </>
+ );
+};
+
+export default ListExportPrintButton;
diff --git a/rsf-admin/src/page/components/listExportPrintUtils.js b/rsf-admin/src/page/components/listExportPrintUtils.js
new file mode 100644
index 0000000..d7b8326
--- /dev/null
+++ b/rsf-admin/src/page/components/listExportPrintUtils.js
@@ -0,0 +1,62 @@
+const EXTEND_FIELD_SOURCE_REGEXP = /^extendFields\.\[(.+)\]$/;
+
+const getExtendFieldKey = (source) => {
+ const match = EXTEND_FIELD_SOURCE_REGEXP.exec(source || '');
+ return match ? match[1] : null;
+};
+
+const reorderColumns = (columns = [], columnRanks = []) => {
+ if (!Array.isArray(columnRanks) || columnRanks.length === 0) {
+ return columns;
+ }
+
+ return columns.reduce((accumulator, column, index) => {
+ const rank = columnRanks.indexOf(index);
+ if (rank === -1) {
+ accumulator[index] = column;
+ } else {
+ accumulator[rank] = column;
+ }
+ return accumulator;
+ }, []).filter(Boolean);
+};
+
+export const resolveVisibleColumns = (
+ storeKey,
+ defaultHiddenColumns = [],
+ columns = [],
+ hiddenColumns = defaultHiddenColumns,
+ columnRanks = []
+) => {
+ if (!storeKey || !Array.isArray(columns)) {
+ return [];
+ }
+
+ const visibleColumns = reorderColumns(columns, columnRanks);
+ const effectiveHiddenColumns = Array.isArray(hiddenColumns) ? hiddenColumns : defaultHiddenColumns;
+
+ return visibleColumns.filter((column) => {
+ if (!column?.source) {
+ return false;
+ }
+ return !effectiveHiddenColumns.includes(column.source);
+ });
+};
+
+export const getValueBySource = (record, source) => {
+ if (!record || !source) {
+ return '';
+ }
+
+ const extendFieldKey = getExtendFieldKey(source);
+ if (extendFieldKey) {
+ return record.extendFields?.[extendFieldKey] ?? '';
+ }
+
+ return source.split('.').reduce((value, key) => {
+ if (value == null) {
+ return undefined;
+ }
+ return value[key];
+ }, record) ?? '';
+};
diff --git a/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx b/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
index a271012..663a76e 100644
--- a/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
+++ b/rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
@@ -1,75 +1,42 @@
-import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
-import { useNavigate } from 'react-router-dom';
+import React, { useState } from "react";
import {
List,
- DatagridConfigurable,
SearchInput,
TopToolbar,
ColumnsButton,
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,
useRefresh,
Button
} from 'react-admin';
-import { Box, Typography, Card, Stack } from '@mui/material';
-import { styled } from '@mui/material/styles';
+import { Box } from '@mui/material';
import WarehouseAreasCreate from "./WarehouseAreasCreate";
-import WarehouseAreasPanel from "./WarehouseAreasPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
-import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import BatchModal from "./BatchModal";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import * as Common from '@/utils/common';
import EditIcon from '@mui/icons-material/Edit';
import StickyDataTable from "@/page/components/StickyDataTable";
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
- '& .css-1vooibu-MuiSvgIcon-root': {
- height: '.9em'
- },
- '& .RaDatagrid-row': {
- cursor: 'auto'
- },
- '& .column-name': {
- },
- '& .opt': {
- width: 200
- },
- '& .MuiTableCell-root': {
- whiteSpace: 'nowrap',
- overflow: 'visible',
- textOverflow: 'unset'
- }
-}));
+import ListExportPrintButton from "../components/ListExportPrintButton";
const filters = [
- <SearchInput source="condition" placeholder="鎼滅储搴撳尯鍚嶇О" alwaysOn />,
+ <SearchInput key="condition" source="condition" placeholder="鎼滅储搴撳尯鍚嶇О" alwaysOn />,
<ReferenceInput
+ key="warehouseId"
source="warehouseId"
label="table.field.loc.warehouseId"
reference="warehouse"
@@ -80,45 +47,140 @@
filterToQuery={(val) => ({ name: val })}
/>
</ReferenceInput>,
- // <TextInput source="uuid" label="table.field.warehouseAreas.uuid" />,
- <TextInput source="code" label="table.field.warehouseAreas.code" />,
- <TextInput source="name" label="table.field.warehouseAreas.name" />,
- <ReferenceInput source="shipperId" label="table.field.warehouseAreas.shipperId" reference="shipper">
- <AutocompleteInput label="table.field.warehouseAreas.shipperId" optionText="name" filterToQuery={(val) => ({ name: val })} />
+ <TextInput key="code" source="code" label="table.field.warehouseAreas.code" />,
+ <TextInput key="name" source="name" label="table.field.warehouseAreas.name" />,
+ <ReferenceInput
+ key="shipperId"
+ source="shipperId"
+ label="table.field.warehouseAreas.shipperId"
+ reference="shipper"
+ >
+ <AutocompleteInput
+ label="table.field.warehouseAreas.shipperId"
+ optionText="name"
+ filterToQuery={(val) => ({ name: val })}
+ />
</ReferenceInput>,
- <NumberInput source="supplierId" label="table.field.warehouseAreas.supplierId" />,
- // <SelectInput source="flagMinus" label="table.field.warehouseAreas.flagMinus"
- // choices={[
- // { id: 0, name: '鍚�' },
- // { id: 1, name: '鏄�' },
- // ]}
- // />,
- // <SelectInput source="flagLabelMange" label="table.field.warehouseAreas.flagLabelMange"
- // choices={[
- // { id: 0, name: ' 鍚�' },
- // { id: 1, name: ' 鏄�' },
- // ]}
- // />,
- // <SelectInput source="flagMix" label="table.field.warehouseAreas.flagMix"
- // choices={[
- // { id: 0, name: '鍚�' },
- // { id: 1, name: '鏄�' },
- // ]}
- // />,
- // <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
- // />,
-]
+ <NumberInput key="supplierId" source="supplierId" label="table.field.warehouseAreas.supplierId" />,
+];
+
+const HIDDEN_COLUMN_SOURCES = [
+ 'id',
+ 'updateTime',
+ 'updateBy',
+ 'createTime',
+ 'createBy',
+ 'longitude',
+ 'latgitude',
+ 'length',
+ 'width',
+ 'height',
+ 'shipperId$',
+ 'supplierId',
+ 'sort',
+];
+
+const createColumnDef = (source, label, fieldType = 'text', extra = {}) => ({
+ source,
+ label,
+ fieldType,
+ hidden: HIDDEN_COLUMN_SOURCES.includes(source),
+ ...extra,
+});
+
+const COLUMN_DEFS = [
+ createColumnDef('id', 'id', 'number'),
+ createColumnDef('sort', 'table.field.warehouseAreas.sort', 'number'),
+ createColumnDef('warehouseId$', 'table.field.warehouseAreas.wareId'),
+ createColumnDef('code', 'table.field.warehouseAreas.code'),
+ createColumnDef('name', 'table.field.warehouseAreas.name'),
+ createColumnDef('type$', 'table.field.warehouseAreas.type'),
+ createColumnDef('shipperId$', 'table.field.warehouseAreas.shipperId'),
+ createColumnDef('supplierId', 'table.field.warehouseAreas.supplierId', 'number'),
+ createColumnDef('flagMix$', 'table.field.warehouseAreas.flagMix'),
+ createColumnDef('flagMinus$', 'table.field.warehouseAreas.flagMinus'),
+ createColumnDef('updateBy', 'common.field.updateBy', 'text', {
+ component: 'reference',
+ reference: 'user',
+ childSource: 'nickname',
+ sortable: false,
+ }),
+ createColumnDef('updateTime', 'common.field.updateTime', 'date', {
+ component: 'date',
+ showTime: true,
+ }),
+ createColumnDef('createBy', 'common.field.createBy', 'text', {
+ component: 'reference',
+ reference: 'user',
+ childSource: 'nickname',
+ sortable: false,
+ }),
+ createColumnDef('createTime', 'common.field.createTime', 'date', {
+ component: 'date',
+ showTime: true,
+ }),
+ createColumnDef('memo', 'common.field.memo', 'text', {
+ sortable: false,
+ }),
+];
+
+const renderColumnField = (column) => {
+ if (column.component === 'reference') {
+ return (
+ <ReferenceField
+ key={column.source}
+ source={column.source}
+ label={column.label}
+ reference={column.reference}
+ link={false}
+ sortable={column.sortable}
+ >
+ <TextField source={column.childSource} />
+ </ReferenceField>
+ );
+ }
+
+ if (column.component === 'date' || column.fieldType === 'date') {
+ return (
+ <DateField
+ key={column.source}
+ source={column.source}
+ label={column.label}
+ showTime={column.showTime}
+ />
+ );
+ }
+
+ if (column.fieldType === 'number') {
+ return (
+ <NumberField
+ key={column.source}
+ source={column.source}
+ label={column.label}
+ />
+ );
+ }
+
+ return (
+ <TextField
+ key={column.source}
+ source={column.source}
+ label={column.label}
+ sortable={column.sortable}
+ />
+ );
+};
+
+const exportPrintButton = (
+ <ListExportPrintButton
+ storeKey="warehouseAreas"
+ columns={COLUMN_DEFS}
+ title="menu.warehouseAreas"
+ fileName="warehouseAreas"
+ />
+);
const WarehouseAreasList = () => {
- const translate = useTranslate();
const [createDialog, setCreateDialog] = useState(false);
const [drawerVal, setDrawerVal] = useState(false);
@@ -133,145 +195,104 @@
}),
marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
}}
- title={"menu.warehouseAreas"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+ title="menu.warehouseAreas"
+ empty={<EmptyData onClick={() => { setCreateDialog(true); }} />}
filters={filters}
sort={{ field: "warehouseId", order: "desc" }}
actions={(
<TopToolbar>
<FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <ColumnsButton storeKey='warehouseAreas' />
- <MyExportButton />
+ <MyCreateButton onClick={() => { setCreateDialog(true); }} />
+ <ColumnsButton storeKey="warehouseAreas" />
+ {exportPrintButton}
</TopToolbar>
)}
perPage={DEFAULT_PAGE_SIZE}
>
<StickyDataTable
- storeKey='warehouseAreas'
- bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
- rowClick={(id, resource, record) => false}
+ storeKey="warehouseAreas"
+ bulkActionButtons={
+ <>
+ {exportPrintButton}
+ <BulkDeleteButton mutationMode={OPERATE_MODE} />
+ </>
+ }
+ rowClick={() => false}
stickyRight={['opt']}
- hiddenColumns={['id', 'updateTime', 'updateBy', 'createTime', 'createBy', 'longitude', 'latgitude', 'length', 'width', 'height', 'shipperId$', 'supplierId', 'sort']}
+ hiddenColumns={HIDDEN_COLUMN_SOURCES}
>
- <NumberField source="id" />
- <NumberField source="sort" label="table.field.warehouseAreas.sort" />
- <TextField source="warehouseId$" label="table.field.warehouseAreas.wareId" />
- <TextField source="code" label="table.field.warehouseAreas.code" />
- <TextField source="name" label="table.field.warehouseAreas.name" />
- <TextField source="type$" label="table.field.warehouseAreas.type"/>
- <TextField source="shipperId$" label="table.field.warehouseAreas.shipperId" />
- <NumberField source="supplierId" label="table.field.warehouseAreas.supplierId" />
- <TextField source="flagMix$" label="table.field.warehouseAreas.flagMix" sortable={false} />
- <TextField source="flagMinus$" label="table.field.warehouseAreas.flagMinus" sortable={false} />
- <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} />
+ {COLUMN_DEFS.map((column) => renderColumnField(column))}
<WrapperField cellClassName="opt" label="common.field.opt">
<EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
<DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
</WrapperField>
-
- {/* <TextField source="flagLabelMange$" label="table.field.warehouseAreas.flagLabelMange" sortable={false} /> */}
- {/* <TextField source="uuid" label="table.field.warehouseAreas.uuid" /> */}
- {/* <ReferenceField source="shipperId" label="table.field.warehouseAreas.shipperId" reference="shipper" link={false} sortable={false}>
- <TextField source="name" />
- </ReferenceField> */}
</StickyDataTable>
-
</List>
<WarehouseAreasCreate
open={createDialog}
setOpen={setCreateDialog}
/>
<PageDrawer
- title='WarehouseAreas Detail'
+ title="WarehouseAreas Detail"
drawerVal={drawerVal}
setDrawerVal={setDrawerVal}
- >
- </PageDrawer>
+ />
</Box>
- )
-}
+ );
+};
export default WarehouseAreasList;
-
const MixButton = () => {
- const record = useRecordContext();
- const notify = useNotify();
- const refresh = useRefresh();
-
-
const [createDialog, setCreateDialog] = useState(false);
return (
<>
- <Button onClick={() => setCreateDialog(true)} label={"toolbar.batchMix"}>
+ <Button onClick={() => setCreateDialog(true)} label="toolbar.batchMix">
<EditIcon />
</Button>
<BatchModal
open={createDialog}
setOpen={setCreateDialog}
- fieldType={'flagMix'}
+ fieldType="flagMix"
/>
</>
-
- )
-}
+ );
+};
const WareButton = () => {
- const record = useRecordContext();
- const notify = useNotify();
- const refresh = useRefresh();
-
-
const [createDialog, setCreateDialog] = useState(false);
return (
<>
- <Button onClick={() => setCreateDialog(true)} label={"toolbar.batchWarehouse"}>
+ <Button onClick={() => setCreateDialog(true)} label="toolbar.batchWarehouse">
<EditIcon />
</Button>
<BatchModal
open={createDialog}
setOpen={setCreateDialog}
- fieldType={'wareId'}
+ fieldType="wareId"
/>
</>
-
- )
-}
+ );
+};
const StatusButton = () => {
- const record = useRecordContext();
- const notify = useNotify();
- const refresh = useRefresh();
-
-
const [createDialog, setCreateDialog] = useState(false);
return (
<>
- <Button onClick={() => setCreateDialog(true)} label={"toolbar.batchStatus"}>
+ <Button onClick={() => setCreateDialog(true)} label="toolbar.batchStatus">
<EditIcon />
</Button>
<BatchModal
open={createDialog}
setOpen={setCreateDialog}
- fieldType={'status'}
+ fieldType="status"
/>
</>
-
- )
-}
+ );
+};
diff --git a/rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx b/rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx
index a2f385e..ba41a6a 100644
--- a/rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx
+++ b/rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useEffect, useState } from "react";
import {
List,
SearchInput,
@@ -20,11 +20,11 @@
import { Box, LinearProgress } from '@mui/material';
import WarehouseAreasItemCreate from "./WarehouseAreasItemCreate";
import request from '@/utils/request';
-import MyExportButton from '../components/MyExportButton';
import { DEFAULT_PAGE_SIZE, PAGE_DRAWER_WIDTH } from '@/config/setting';
import StickyDataTable from "../components/StickyDataTable";
import WarehouseIsptResult from "./WarehouseIsptResult";
import useTableLayout from '@/utils/useTableLayout';
+import ListExportPrintButton from "../components/ListExportPrintButton";
const baseFilters = [
<SearchInput key="condition" source="condition" alwaysOn />,
@@ -50,7 +50,6 @@
<NumberInput key="weight" source="weight" label="table.field.warehouseAreasItem.weight" />,
<TextInput key="prodTime" source="prodTime" label="table.field.warehouseAreasItem.prodTime" />,
<TextInput key="splrBtch" source="splrBtch" label="table.field.warehouseAreasItem.splrBtch" />,
-
<TextInput key="memo" label="common.field.memo" source="memo" />,
<SelectInput
key="status"
@@ -66,61 +65,132 @@
const baseFilterSources = new Set(baseFilters.map((item) => item.props.source).filter(Boolean));
-const hiddenColumns = [
+const hiddenColumnSources = [
'prodTime', 'platOrderCode', 'id', 'createTime', 'memo', 'areaId', 'brand',
'weight', 'splrId', 'projectCode', 'statusBool', 'extendFields.[priceUnitId]', 'isptResult$',
'extendFields.[inStockType]', 'matnrId', 'trackCode', 'workQty', 'batch', 'shipperId',
'isptResult', 'createBy$', 'extendFields.[baseUnitId]'
];
-const baseColumns = [
- <NumberField key="id" source="id" />,
- <TextField key="areaName" source="areaName" label="鏀惰揣鍖哄悕绉�" />,
- <TextField key="asnCode" source="asnCode" label="table.field.warehouseAreasItem.asnCode" />,
- <TextField key="platWorkCode" source="platWorkCode" label="table.field.asnOrderItem.platWorkCode" />,
- <TextField key="platItemId" source="platItemId" label="table.field.deliveryItem.platItemId" />,
- <NumberField key="matnrId" source="matnrId" label="table.field.warehouseAreasItem.matnrId" />,
- <TextField key="matnrCode" source="matnrCode" label="table.field.warehouseAreasItem.matnrCode" />,
- <TextField key="maktx" source="maktx" label="table.field.warehouseAreasItem.matnrName" />,
- <TextField key="splrBatch" source="splrBatch" label="table.field.warehouseAreasItem.splrBtch" />,
- <TextField key="batch" source="batch" label="table.field.warehouseAreasItem.batch" />,
- <TextField key="trackCode" source="trackCode" label="table.field.warehouseAreasItem.barcode" />,
- <TextField key="unit" source="unit" label="table.field.warehouseAreasItem.unit" />,
- <NumberField key="anfme" source="anfme" label="table.field.warehouseAreasItem.anfme" />,
- <NumberField key="workQty" source="workQty" label="table.field.warehouseAreasItem.workQty" />,
- <NumberField key="ableQty" source="ableQty" label="table.field.warehouseAreasItem.qty" />,
- <TextField key="platOrderCode" source="platOrderCode" label="table.field.asnOrderItem.platOrderCode" />,
- <TextField key="projectCode" source="projectCode" label="table.field.asnOrderItem.projectCode" />,
- <TextField key="brand" source="brand" label="table.field.warehouseAreasItem.brand" />,
- <TextField key="shipperId" source="shipperId" label="table.field.warehouseAreasItem.shipperId" />,
- <TextField key="splrId" source="splrId" label="table.field.warehouseAreasItem.splrId" />,
- <TextField key="isptResult" source="isptResult$" label="table.field.warehouseAreasItem.isptResult" sortable={false} />,
- <NumberField key="weight" source="weight" label="table.field.warehouseAreasItem.weight" />,
- <TextField key="prodTime" source="prodTime" label="table.field.warehouseAreasItem.prodTime" />,
+const createColumnDef = (source, label, fieldType = 'text', options = {}) => ({
+ source,
+ label,
+ fieldType,
+ hidden: hiddenColumnSources.includes(source),
+ sortable: options.sortable,
+});
+
+const baseColumnDefs = [
+ createColumnDef('id', 'table.field.warehouseAreasItem.id', 'number'),
+ createColumnDef('areaName', 'table.field.warehouseAreasItem.areaName'),
+ createColumnDef('asnCode', 'table.field.warehouseAreasItem.asnCode'),
+ createColumnDef('platWorkCode', 'table.field.asnOrderItem.platWorkCode'),
+ createColumnDef('platItemId', 'table.field.deliveryItem.platItemId'),
+ createColumnDef('matnrId', 'table.field.warehouseAreasItem.matnrId', 'number'),
+ createColumnDef('matnrCode', 'table.field.warehouseAreasItem.matnrCode'),
+ createColumnDef('maktx', 'table.field.warehouseAreasItem.matnrName'),
+ createColumnDef('splrBatch', 'table.field.warehouseAreasItem.splrBtch'),
+ createColumnDef('batch', 'table.field.warehouseAreasItem.batch'),
+ createColumnDef('trackCode', 'table.field.warehouseAreasItem.barcode'),
+ createColumnDef('unit', 'table.field.warehouseAreasItem.unit'),
+ createColumnDef('anfme', 'table.field.warehouseAreasItem.anfme', 'number'),
+ createColumnDef('workQty', 'table.field.warehouseAreasItem.workQty', 'number'),
+ createColumnDef('ableQty', 'table.field.warehouseAreasItem.qty', 'number'),
+ createColumnDef('platOrderCode', 'table.field.asnOrderItem.platOrderCode'),
+ createColumnDef('projectCode', 'table.field.asnOrderItem.projectCode'),
+ createColumnDef('brand', 'table.field.warehouseAreasItem.brand'),
+ createColumnDef('shipperId', 'table.field.warehouseAreasItem.shipperId'),
+ createColumnDef('splrId', 'table.field.warehouseAreasItem.splrId'),
+ createColumnDef('isptResult$', 'table.field.warehouseAreasItem.isptResult', 'text', { sortable: false }),
+ createColumnDef('weight', 'table.field.warehouseAreasItem.weight', 'number'),
+ createColumnDef('prodTime', 'table.field.warehouseAreasItem.prodTime'),
];
-const trailingColumns = [
- <TextField key="updateBy" source="updateBy$" label="common.field.updateBy" />,
- <DateField key="updateTime" source="updateTime" label="common.field.updateTime" showTime />,
- <TextField key="createBy" source="createBy$" label="common.field.createBy" />,
- <DateField key="createTime" source="createTime" label="common.field.createTime" showTime />,
- <BooleanField key="statusBool" source="statusBool" label="common.field.status" sortable={false} />,
- <TextField key="memo" source="memo" label="common.field.memo" sortable={false} />,
+const trailingColumnDefs = [
+ createColumnDef('updateBy$', 'common.field.updateBy'),
+ createColumnDef('updateTime', 'common.field.updateTime', 'date'),
+ createColumnDef('createBy$', 'common.field.createBy'),
+ createColumnDef('createTime', 'common.field.createTime', 'date'),
+ createColumnDef('statusBool', 'common.field.status', 'boolean', { sortable: false }),
+ createColumnDef('memo', 'common.field.memo', 'text', { sortable: false }),
];
const buildDynamicFilter = (field) => (
<TextInput key={field.fields} source={field.fields} label={field.fieldsAlise} />
);
-const buildDynamicColumn = (field) => (
- <TextField key={field.fields} source={`extendFields.[${field.fields}]`} label={field.fieldsAlise} />
+const buildDynamicColumnDef = (field) => createColumnDef(
+ `extendFields.[${field.fields}]`,
+ field.fieldsAlise
);
+const buildFieldElement = (column) => {
+ const commonProps = {
+ key: column.source,
+ source: column.source,
+ label: column.label,
+ };
+
+ if (column.sortable === false) {
+ commonProps.sortable = false;
+ }
+
+ switch (column.fieldType) {
+ case 'number':
+ return <NumberField {...commonProps} />;
+ case 'date':
+ return <DateField {...commonProps} showTime />;
+ case 'boolean':
+ return <BooleanField {...commonProps} />;
+ default:
+ return <TextField {...commonProps} />;
+ }
+};
+
const WarehouseAreasItemList = () => {
- const [itemInfo, setItemInfo] = useState({})
+ const notify = useNotify();
+ const [itemInfo, setItemInfo] = useState({});
const [createDialog, setCreateDialog] = useState(false);
const [drawerVal, setDrawerVal] = useState(false);
const [dynamicFilters, setDynamicFilters] = useState([]);
+ const [columnDefs, setColumnDefs] = useState([]);
+
+ useEffect(() => {
+ let active = true;
+
+ const getDynamicFields = async () => {
+ try {
+ const { data: { code, data, msg } } = await request.get("/fields/enable/list");
+ if (!active) {
+ return;
+ }
+ if (code === 200) {
+ const dynamicColumnDefs = data.map(buildDynamicColumnDef);
+ const nextDynamicFilters = data
+ .filter((field) => !baseFilterSources.has(field.fields))
+ .map(buildDynamicFilter);
+
+ setColumnDefs([...baseColumnDefs, ...dynamicColumnDefs, ...trailingColumnDefs]);
+ setDynamicFilters(nextDynamicFilters);
+ } else {
+ setColumnDefs([...baseColumnDefs, ...trailingColumnDefs]);
+ notify(msg);
+ }
+ } catch (error) {
+ if (active) {
+ setColumnDefs([...baseColumnDefs, ...trailingColumnDefs]);
+ notify('璇锋眰鍑洪敊');
+ }
+ }
+ };
+
+ getDynamicFields();
+
+ return () => {
+ active = false;
+ setDynamicFilters([]);
+ };
+ }, [notify]);
return (
<Box display="flex">
@@ -139,8 +209,16 @@
actions={(
<TopToolbar>
<FilterButton />
- <ColumnsButton storeKey='warehouseAreasItem' />
- <MyExportButton />
+ {columnDefs.length > 0 && (
+ <ColumnsButton storeKey='warehouseAreasItem' />
+ )}
+ {columnDefs.length > 0 && (
+ <ListExportPrintButton
+ storeKey='warehouseAreasItem'
+ columns={columnDefs}
+ title='menu.warehouseAreasItem'
+ />
+ )}
</TopToolbar>
)}
filters={[...baseFilters, ...dynamicFilters]}
@@ -148,7 +226,7 @@
>
<DynamicFields
drawerOpen={!!drawerVal}
- onDynamicFiltersChange={setDynamicFilters}
+ columnDefs={columnDefs}
/>
</List>
<WarehouseAreasItemCreate
@@ -162,12 +240,6 @@
setDrawerVal={setDrawerVal}
>
</WarehouseIsptResult>
- {/* <PageDrawer
- title='WarehouseAreasItem Detail'
- drawerVal={drawerVal}
- setDrawerVal={setDrawerVal}
- >
- </PageDrawer> */}
</Box>
)
}
@@ -175,46 +247,10 @@
export default WarehouseAreasItemList;
-const DynamicFields = ({ drawerOpen, onDynamicFiltersChange }) => {
- const notify = useNotify();
- const [columns, setColumns] = useState([]);
+const DynamicFields = ({ drawerOpen, columnDefs }) => {
const { isLoading } = useListContext();
const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerOpen);
-
- useEffect(() => {
- let active = true;
-
- const getDynamicFields = async () => {
- try {
- const { data: { code, data, msg }, } = await request.get("/fields/enable/list");
- if (!active) {
- return;
- }
- if (code === 200) {
- const dynamicColumns = data.map(buildDynamicColumn);
- const nextDynamicFilters = data
- .filter((field) => !baseFilterSources.has(field.fields))
- .map(buildDynamicFilter);
-
- setColumns([...baseColumns, ...dynamicColumns, ...trailingColumns]);
- onDynamicFiltersChange(nextDynamicFilters);
- } else {
- notify(msg);
- }
- } catch (error) {
- if (active) {
- notify('璇锋眰鍑洪敊');
- }
- }
- };
-
- getDynamicFields();
-
- return () => {
- active = false;
- onDynamicFiltersChange([]);
- };
- }, [notify, onDynamicFiltersChange]);
+ const columns = columnDefs.map(buildFieldElement);
return (
<Box sx={{
@@ -241,9 +277,15 @@
{columns.length > 0 &&
<StickyDataTable
storeKey='warehouseAreasItem'
- bulkActionButtons={false}
+ bulkActionButtons={(
+ <ListExportPrintButton
+ storeKey='warehouseAreasItem'
+ columns={columnDefs}
+ title='menu.warehouseAreasItem'
+ />
+ )}
rowClick={false}
- hiddenColumns={hiddenColumns}
+ hiddenColumns={hiddenColumnSources}
>
{columns}
</StickyDataTable>}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportHandler.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportHandler.java
new file mode 100644
index 0000000..da4a548
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportHandler.java
@@ -0,0 +1,20 @@
+package com.vincent.rsf.server.common.service;
+
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ListExportHandler<T, P extends BaseParam> {
+ List<T> listByIds(List<Long> ids);
+
+ List<T> listByFilter(Map<String, Object> sanitizedMap, P baseParam);
+
+ default void fillExportFields(List<T> records) {
+ }
+
+ Map<String, Object> toExportRow(T record, List<ExcelUtil.ExportColumn> columns);
+
+ String defaultReportTitle();
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportService.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportService.java
new file mode 100644
index 0000000..b661c90
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/service/ListExportService.java
@@ -0,0 +1,154 @@
+package com.vincent.rsf.server.common.service;
+
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
+import org.springframework.stereotype.Service;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+
+@Service
+public class ListExportService {
+ private static final Set<String> EXPORT_ONLY_KEYS = Set.of(
+ "columns",
+ "reportTitle",
+ "reportDate",
+ "printedAt",
+ "operator",
+ "count",
+ "report_title",
+ "report_date",
+ "printed_at"
+ );
+
+ public <T, P extends BaseParam> void export(
+ Map<String, Object> map,
+ Function<Map<String, Object>, P> paramBuilder,
+ ListExportHandler<T, P> exportHandler,
+ HttpServletResponse response
+ ) throws Exception {
+ Map<String, Object> sanitizedMap = sanitizeExportMap(map);
+ P baseParam = paramBuilder.apply(sanitizedMap);
+ List<ExcelUtil.ExportColumn> columns = buildExportColumns(map);
+ if (columns.isEmpty()) {
+ throw new CoolException("瀵煎嚭鍒椾笉鑳戒负绌�");
+ }
+
+ List<T> records = getExportRecords(sanitizedMap, baseParam, exportHandler);
+ exportHandler.fillExportFields(records);
+
+ List<Map<String, Object>> rows = records.stream()
+ .map(record -> exportHandler.toExportRow(record, columns))
+ .toList();
+
+ ExcelUtil.ExportMeta exportMeta = buildExportMeta(map, rows.size(), exportHandler.defaultReportTitle());
+ ExcelUtil.build(ExcelUtil.create(rows, columns, exportMeta), response);
+ }
+
+ private Map<String, Object> sanitizeExportMap(Map<String, Object> map) {
+ Map<String, Object> exportMap = new HashMap<>(map);
+ EXPORT_ONLY_KEYS.forEach(exportMap::remove);
+ exportMap.remove("meta");
+
+ sanitizeNestedMap(exportMap, "filter");
+ sanitizeNestedMap(exportMap, "filterValues");
+ return exportMap;
+ }
+
+ private void sanitizeNestedMap(Map<String, Object> exportMap, String key) {
+ Object nestedObject = exportMap.get(key);
+ if (nestedObject instanceof Map<?, ?> nestedMap) {
+ Map<String, Object> sanitizedMap = new HashMap<>();
+ for (Map.Entry<?, ?> entry : nestedMap.entrySet()) {
+ String nestedKey = String.valueOf(entry.getKey());
+ if (!EXPORT_ONLY_KEYS.contains(nestedKey)) {
+ sanitizedMap.put(nestedKey, entry.getValue());
+ }
+ }
+ exportMap.put(key, sanitizedMap);
+ }
+ }
+
+ private List<ExcelUtil.ExportColumn> buildExportColumns(Map<String, Object> map) {
+ Object columnsObject = map.get("columns");
+ if (Objects.isNull(columnsObject)) {
+ Object metaObject = map.get("meta");
+ if (metaObject instanceof Map<?, ?> metaMap) {
+ columnsObject = metaMap.get("columns");
+ }
+ }
+
+ if (!(columnsObject instanceof List<?> rawColumns)) {
+ return Collections.emptyList();
+ }
+
+ List<ExcelUtil.ExportColumn> columns = new ArrayList<>();
+ for (Object rawColumn : rawColumns) {
+ if (!(rawColumn instanceof Map<?, ?> columnMap)) {
+ continue;
+ }
+ Object source = columnMap.get("source");
+ Object label = columnMap.get("label");
+ if (Objects.isNull(source) || Objects.isNull(label)) {
+ continue;
+ }
+ columns.add(new ExcelUtil.ExportColumn(String.valueOf(source), String.valueOf(label)));
+ }
+ return columns;
+ }
+
+ private <T, P extends BaseParam> List<T> getExportRecords(
+ Map<String, Object> sanitizedMap,
+ P baseParam,
+ ListExportHandler<T, P> exportHandler
+ ) {
+ List<Long> ids = parseExportIds(sanitizedMap.get("ids"));
+ if (!ids.isEmpty()) {
+ return exportHandler.listByIds(ids);
+ }
+ return exportHandler.listByFilter(sanitizedMap, baseParam);
+ }
+
+ private List<Long> parseExportIds(Object idsObject) {
+ if (idsObject instanceof List<?> ids && !ids.isEmpty()) {
+ return ids.stream()
+ .map(String::valueOf)
+ .map(Long::valueOf)
+ .toList();
+ }
+ if (idsObject instanceof Object[] ids && ids.length > 0) {
+ List<Long> exportIds = new ArrayList<>();
+ for (Object id : ids) {
+ exportIds.add(Long.valueOf(String.valueOf(id)));
+ }
+ return exportIds;
+ }
+ return Collections.emptyList();
+ }
+
+ private ExcelUtil.ExportMeta buildExportMeta(Map<String, Object> map, int count, String defaultReportTitle) {
+ Object metaObject = map.get("meta");
+ if (!(metaObject instanceof Map<?, ?> metaMap)) {
+ return new ExcelUtil.ExportMeta(defaultReportTitle, "", "", "", count);
+ }
+
+ String reportTitle = getMetaValue(metaMap, "reportTitle", defaultReportTitle);
+ String reportDate = getMetaValue(metaMap, "reportDate", "");
+ String printedAt = getMetaValue(metaMap, "printedAt", "");
+ String operator = getMetaValue(metaMap, "operator", "");
+ return new ExcelUtil.ExportMeta(reportTitle, reportDate, printedAt, operator, count);
+ }
+
+ private String getMetaValue(Map<?, ?> metaMap, String key, String defaultValue) {
+ Object value = metaMap.get(key);
+ return Objects.isNull(value) ? defaultValue : String.valueOf(value);
+ }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java
index 8d81944..4b3649a 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/utils/ExcelUtil.java
@@ -15,6 +15,7 @@
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.ss.util.CellRangeAddress;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -23,12 +24,16 @@
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Created by vincent on 2/17/2024
*/
@Slf4j
public class ExcelUtil {
+ private static final Pattern EXTEND_FIELD_SOURCE_PATTERN = Pattern.compile("^extendFields\\.\\[(.+)]$");
+
public static void build(Workbook workbook, HttpServletResponse response) {
response.reset();
Http.cors(response);
@@ -112,14 +117,7 @@
} catch (IllegalAccessException e) {
e.printStackTrace();
}
- if (value != null) {
- if (value instanceof Date) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- row.createCell(cellIndex).setCellValue(sdf.format((Date) value));
- } else {
- row.createCell(cellIndex).setCellValue(value.toString());
- }
- }
+ writeCellValue(row, cellIndex, value);
cellIndex++;
}
}
@@ -131,6 +129,208 @@
return workbook;
}
+ public static Workbook create(List<Map<String, Object>> rows, List<ExportColumn> columns) {
+ return create(rows, columns, null);
+ }
+
+ public static Workbook create(List<Map<String, Object>> rows, List<ExportColumn> columns, ExportMeta exportMeta) {
+ XSSFWorkbook workbook = new XSSFWorkbook();
+ Sheet sheet = workbook.createSheet("export");
+ int titleColumnCount = Math.max(columns.size(), 4);
+ int currentRowIndex = 0;
+
+ CellStyle titleStyle = createTitleStyle(workbook);
+ CellStyle subHeaderStyle = createSubHeaderStyle(workbook);
+ CellStyle headerStyle = createHeaderStyle(workbook);
+ CellStyle bodyStyle = createBodyStyle(workbook);
+
+ if (exportMeta != null && StringUtils.isNotBlank(exportMeta.getReportTitle())) {
+ Row titleRow = sheet.createRow(currentRowIndex++);
+ titleRow.setHeightInPoints(24);
+ Cell titleCell = titleRow.createCell(0);
+ titleCell.setCellValue(exportMeta.getReportTitle());
+ titleCell.setCellStyle(titleStyle);
+ sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, titleColumnCount - 1));
+
+ Row subHeaderRow = sheet.createRow(currentRowIndex++);
+ writeSubHeaderCell(subHeaderRow, 0, getSubHeaderText("鎶ヨ〃鏃ユ湡", exportMeta.getReportDate()), subHeaderStyle);
+ writeSubHeaderCell(subHeaderRow, 1, getSubHeaderText("鎵撳嵃浜�", exportMeta.getOperator()), subHeaderStyle);
+ writeSubHeaderCell(subHeaderRow, 2, getSubHeaderText("鎵撳嵃鏃堕棿", exportMeta.getPrintedAt()), subHeaderStyle);
+ writeSubHeaderCell(subHeaderRow, 3, getSubHeaderText("璁板綍鏁�", String.valueOf(exportMeta.getCount())), subHeaderStyle);
+
+ currentRowIndex++;
+ }
+
+ Row header = sheet.createRow(currentRowIndex++);
+ for (int index = 0; index < columns.size(); index++) {
+ Cell headerCell = header.createCell(index);
+ headerCell.setCellValue(columns.get(index).getLabel());
+ headerCell.setCellStyle(headerStyle);
+ }
+
+ int rowIndex = currentRowIndex;
+ for (Map<String, Object> rowData : rows) {
+ Row row = sheet.createRow(rowIndex++);
+ for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
+ Object value = getRowValue(rowData, columns.get(columnIndex).getSource());
+ writeCellValue(row, columnIndex, value, bodyStyle);
+ }
+ }
+
+ for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
+ sheet.autoSizeColumn(columnIndex);
+ }
+
+ return workbook;
+ }
+
+ private static Object getRowValue(Map<String, Object> rowData, String source) {
+ if (rowData == null || StringUtils.isBlank(source)) {
+ return null;
+ }
+
+ if (rowData.containsKey(source)) {
+ return rowData.get(source);
+ }
+
+ Matcher matcher = EXTEND_FIELD_SOURCE_PATTERN.matcher(source);
+ if (matcher.matches()) {
+ Object extendFields = rowData.get("extendFields");
+ if (extendFields instanceof Map<?, ?> extendFieldMap) {
+ return extendFieldMap.get(matcher.group(1));
+ }
+ }
+
+ return rowData.get(source);
+ }
+
+ private static void writeCellValue(Row row, int cellIndex, Object value) {
+ writeCellValue(row, cellIndex, value, null);
+ }
+
+ private static void writeCellValue(Row row, int cellIndex, Object value, CellStyle cellStyle) {
+ Cell cell = row.createCell(cellIndex);
+ if (cellStyle != null) {
+ cell.setCellStyle(cellStyle);
+ }
+ if (value == null) {
+ return;
+ }
+ if (value instanceof Date) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ cell.setCellValue(sdf.format((Date) value));
+ return;
+ }
+ cell.setCellValue(value.toString());
+ }
+
+ private static void writeSubHeaderCell(Row row, int cellIndex, String value, CellStyle cellStyle) {
+ Cell cell = row.createCell(cellIndex);
+ cell.setCellValue(value);
+ cell.setCellStyle(cellStyle);
+ }
+
+ private static String getSubHeaderText(String label, String value) {
+ return label + ": " + StringUtils.defaultString(value);
+ }
+
+ private static CellStyle createTitleStyle(Workbook workbook) {
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.CENTER);
+ cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+ Font font = workbook.createFont();
+ font.setBold(true);
+ font.setFontHeightInPoints((short) 14);
+ cellStyle.setFont(font);
+ return cellStyle;
+ }
+
+ private static CellStyle createSubHeaderStyle(Workbook workbook) {
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.LEFT);
+ cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+ Font font = workbook.createFont();
+ font.setFontHeightInPoints((short) 10);
+ cellStyle.setFont(font);
+ return cellStyle;
+ }
+
+ private static CellStyle createHeaderStyle(Workbook workbook) {
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.CENTER);
+ cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+ Font font = workbook.createFont();
+ font.setBold(true);
+ font.setFontHeightInPoints((short) 10);
+ cellStyle.setFont(font);
+ return cellStyle;
+ }
+
+ private static CellStyle createBodyStyle(Workbook workbook) {
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.LEFT);
+ cellStyle.setVerticalAlignment(VerticalAlignment.TOP);
+ cellStyle.setWrapText(true);
+ return cellStyle;
+ }
+
+ public static class ExportColumn {
+ private final String source;
+ private final String label;
+
+ public ExportColumn(String source, String label) {
+ this.source = source;
+ this.label = label;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+ }
+
+ public static class ExportMeta {
+ private final String reportTitle;
+ private final String reportDate;
+ private final String printedAt;
+ private final String operator;
+ private final int count;
+
+ public ExportMeta(String reportTitle, String reportDate, String printedAt, String operator, int count) {
+ this.reportTitle = reportTitle;
+ this.reportDate = reportDate;
+ this.printedAt = printedAt;
+ this.operator = operator;
+ this.count = count;
+ }
+
+ public String getReportTitle() {
+ return reportTitle;
+ }
+
+ public String getReportDate() {
+ return reportDate;
+ }
+
+ public String getPrintedAt() {
+ return printedAt;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public int getCount() {
+ return count;
+ }
+ }
+
/**
* 娣诲姞瀵煎叆excel閰嶇疆鍙傛暟
* 娉細榛樿閰嶇疆鍙弧瓒冲綋鍓嶉渶姹�
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasController.java
index 0f18db4..85efc65 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasController.java
@@ -1,15 +1,18 @@
package com.vincent.rsf.server.manager.controller;
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.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
-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.common.service.ListExportHandler;
+import com.vincent.rsf.server.common.service.ListExportService;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.manager.controller.params.WarehouseAreaParam;
import com.vincent.rsf.server.manager.entity.Loc;
import com.vincent.rsf.server.manager.entity.WarehouseAreas;
@@ -18,6 +21,8 @@
import com.vincent.rsf.server.system.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -34,6 +39,42 @@
@Autowired
private LocService locService;
+
+ @Autowired
+ private ListExportService listExportService;
+
+ private final ListExportHandler<WarehouseAreas, BaseParam> warehouseAreasExportHandler = new ListExportHandler<>() {
+ @Override
+ public List<WarehouseAreas> listByIds(List<Long> ids) {
+ return warehouseAreasService.listByIds(ids);
+ }
+
+ @Override
+ public List<WarehouseAreas> listByFilter(Map<String, Object> sanitizedMap, BaseParam baseParam) {
+ PageParam<WarehouseAreas, BaseParam> pageParam = new PageParam<>(baseParam, WarehouseAreas.class);
+ QueryWrapper<WarehouseAreas> queryWrapper = pageParam.buildWrapper(true);
+ return warehouseAreasService.list(queryWrapper);
+ }
+
+ @Override
+ public Map<String, Object> toExportRow(WarehouseAreas record, List<ExcelUtil.ExportColumn> columns) {
+ BeanWrapper beanWrapper = new BeanWrapperImpl(record);
+ Map<String, Object> row = new LinkedHashMap<>();
+
+ for (ExcelUtil.ExportColumn column : columns) {
+ if (beanWrapper.isReadableProperty(column.getSource())) {
+ row.put(column.getSource(), beanWrapper.getPropertyValue(column.getSource()));
+ }
+ }
+
+ return row;
+ }
+
+ @Override
+ public String defaultReportTitle() {
+ return "浠撳簱搴撳尯鎶ヨ〃";
+ }
+ };
@PreAuthorize("hasAuthority('manager:warehouseAreas:list')")
@PostMapping("/warehouseAreas/page")
@@ -170,7 +211,12 @@
@PreAuthorize("hasAuthority('manager:warehouseAreas:list')")
@PostMapping("/warehouseAreas/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(warehouseAreasService.list(), WarehouseAreas.class), response);
+ listExportService.export(
+ map,
+ exportMap -> buildParam(exportMap, BaseParam.class),
+ warehouseAreasExportHandler,
+ response
+ );
}
}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasItemController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasItemController.java
index f468a2d..b022be8 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasItemController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseAreasItemController.java
@@ -6,16 +6,20 @@
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.common.service.ListExportHandler;
+import com.vincent.rsf.server.common.service.ListExportService;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
import com.vincent.rsf.server.common.utils.FieldsUtils;
import com.vincent.rsf.server.manager.entity.WarehouseAreasItem;
import com.vincent.rsf.server.manager.service.WarehouseAreasItemService;
import com.vincent.rsf.server.system.controller.BaseController;
import io.swagger.annotations.Api;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -26,9 +30,41 @@
@Api(tags = "搴撳尯搴撳瓨鏄庣粏")
@RestController
public class WarehouseAreasItemController extends BaseController {
-
@Autowired
private WarehouseAreasItemService warehouseAreasItemService;
+
+ @Autowired
+ private ListExportService listExportService;
+
+ private final ListExportHandler<WarehouseAreasItem, BaseParam> warehouseAreasItemExportHandler = new ListExportHandler<>() {
+ @Override
+ public List<WarehouseAreasItem> listByIds(List<Long> ids) {
+ return warehouseAreasItemService.listByIds(ids);
+ }
+
+ @Override
+ public List<WarehouseAreasItem> listByFilter(Map<String, Object> sanitizedMap, BaseParam baseParam) {
+ PageParam<WarehouseAreasItem, BaseParam> pageParam = new PageParam<>(baseParam, WarehouseAreasItem.class);
+ QueryWrapper<WarehouseAreasItem> queryWrapper = pageParam.buildWrapper(true);
+ FieldsUtils.setFieldsFilters(queryWrapper, pageParam, WarehouseAreasItem.class);
+ return warehouseAreasItemService.list(queryWrapper);
+ }
+
+ @Override
+ public void fillExportFields(List<WarehouseAreasItem> records) {
+ fillExtendFields(records);
+ }
+
+ @Override
+ public Map<String, Object> toExportRow(WarehouseAreasItem record, List<ExcelUtil.ExportColumn> columns) {
+ return buildExportRow(record, columns);
+ }
+
+ @Override
+ public String defaultReportTitle() {
+ return "鏀惰揣搴撳瓨鎶ヨ〃";
+ }
+ };
@PreAuthorize("hasAuthority('manager:warehouseAreasItem:list')")
@PostMapping("/warehouseAreasItem/page")
@@ -141,7 +177,37 @@
@PreAuthorize("hasAuthority('manager:warehouseAreasItem:list')")
@PostMapping("/warehouseAreasItem/export")
public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(warehouseAreasItemService.list(), WarehouseAreasItem.class), response);
+ listExportService.export(
+ map,
+ exportMap -> buildParam(exportMap, BaseParam.class),
+ warehouseAreasItemExportHandler,
+ response
+ );
+ }
+
+ private void fillExtendFields(List<WarehouseAreasItem> records) {
+ for (WarehouseAreasItem record : records) {
+ if (!Objects.isNull(record.getFieldsIndex())) {
+ Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex());
+ record.setExtendFields(fields);
+ }
+ }
+ }
+ private Map<String, Object> buildExportRow(WarehouseAreasItem record, List<ExcelUtil.ExportColumn> columns) {
+ BeanWrapper beanWrapper = new BeanWrapperImpl(record);
+ Map<String, Object> row = new LinkedHashMap<>();
+ row.put("extendFields", record.getExtendFields());
+
+ for (ExcelUtil.ExportColumn column : columns) {
+ if (column.getSource().startsWith("extendFields.[")) {
+ continue;
+ }
+ if (beanWrapper.isReadableProperty(column.getSource())) {
+ row.put(column.getSource(), beanWrapper.getPropertyValue(column.getSource()));
+ }
+ }
+
+ return row;
}
}
--
Gitblit v1.9.1