From 9e84e0199af37546a5fe76befdf004fae84ca921 Mon Sep 17 00:00:00 2001
From: chen.lin <1442464845@qq.com>
Date: 星期三, 25 二月 2026 13:36:34 +0800
Subject: [PATCH] 站点初始化,批量增加

---
 rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx      |   18 ++
 rsf-admin/src/page/basicInfo/basStation/BasStationInitModal.jsx |  363 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 380 insertions(+), 1 deletions(-)

diff --git a/rsf-admin/src/page/basicInfo/basStation/BasStationInitModal.jsx b/rsf-admin/src/page/basicInfo/basStation/BasStationInitModal.jsx
new file mode 100644
index 0000000..865aa26
--- /dev/null
+++ b/rsf-admin/src/page/basicInfo/basStation/BasStationInitModal.jsx
@@ -0,0 +1,363 @@
+import React, { useState, useEffect } from "react";
+import { useTranslate, useNotify, useRefresh } from 'react-admin';
+import {
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogTitle,
+  Box,
+  Button,
+  IconButton,
+  Table,
+  TableBody,
+  TableCell,
+  TableContainer,
+  TableHead,
+  TableRow,
+  Paper,
+  Grid,
+  TextField,
+  MenuItem,
+  Select,
+  FormControl,
+  InputLabel,
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import AddIcon from '@mui/icons-material/Add';
+import DeleteIcon from '@mui/icons-material/Delete';
+import SaveIcon from '@mui/icons-material/Save';
+import request from '@/utils/request';
+
+const defaultPair = () => ({ stationName: '', stationId: '' });
+
+const BasStationInitModal = ({ open, setOpen }) => {
+  const translate = useTranslate();
+  const notify = useNotify();
+  const refresh = useRefresh();
+
+  const [pairs, setPairs] = useState([defaultPair()]);
+  const [common, setCommon] = useState({
+    type: 0,
+    useStatus: '',
+    areaIds: [],
+    containerTypes: [],
+    inAble: 0,
+    outAble: 0,
+  });
+  const [loading, setLoading] = useState(false);
+
+  const [useStatusOptions, setUseStatusOptions] = useState([]);
+  const [areaOptions, setAreaOptions] = useState([]);
+  const [containerTypeOptions, setContainerTypeOptions] = useState([]);
+
+  useEffect(() => {
+    if (!open) return;
+    request.post('/dictData/page', { dictTypeCode: 'sys_sta_use_stas', current: 1, pageSize: 100 })
+      .then((res) => {
+        if (res?.data?.code === 200 && res?.data?.data?.records) {
+          setUseStatusOptions(res.data.data.records.map((item) => ({
+            value: item.value,
+            label: item.label ?? item.value,
+          })));
+        }
+      })
+      .catch(() => {});
+  }, [open]);
+
+  useEffect(() => {
+    if (!open) return;
+    request.post('/warehouseAreas/list', {})
+      .then((res) => {
+        if (res?.data?.code === 200 && res?.data?.data) {
+          const list = Array.isArray(res.data.data) ? res.data.data : (res.data.data?.records || []);
+          setAreaOptions(list.map((item) => ({ id: item.id, name: item.name ?? item.id })));
+        }
+      })
+      .catch(() => {});
+  }, [open]);
+
+  useEffect(() => {
+    if (!open) return;
+    request.post('/dictData/page', { dictTypeCode: 'sys_container_type', current: 1, pageSize: 100 })
+      .then((res) => {
+        if (res?.data?.code === 200 && res?.data?.data?.records) {
+          setContainerTypeOptions(res.data.data.records.map((item) => ({
+            id: item.id,
+            label: item.label ?? item.value ?? item.id,
+          })));
+        }
+      })
+      .catch(() => {});
+  }, [open]);
+
+  const handleClose = (event, reason) => {
+    if (reason !== "backdropClick") {
+      setOpen(false);
+      setPairs([defaultPair()]);
+    }
+  };
+
+  const addRow = () => setPairs((prev) => [...prev, defaultPair()]);
+  const removeRow = (index) => {
+    if (pairs.length <= 1) return;
+    setPairs((prev) => prev.filter((_, i) => i !== index));
+  };
+  const changePair = (index, field, value) => {
+    setPairs((prev) => prev.map((p, i) => (i === index ? { ...p, [field]: value } : p)));
+  };
+
+  const handleSubmit = async () => {
+    const toSave = pairs.filter(
+      (p) => (p.stationName || '').trim() && (p.stationId || '').trim()
+    );
+    if (toSave.length === 0) {
+      notify('璇疯嚦灏戝~鍐欎竴缁勭珯鐐瑰悕绉板拰绔欑偣缂栫爜', { type: 'error' });
+      return;
+    }
+    if (!(common.areaIds?.length)) {
+      notify('璇烽�夋嫨鍙法搴撳尯', { type: 'error' });
+      return;
+    }
+    if (!(common.containerTypes?.length)) {
+      notify('璇烽�夋嫨瀹瑰櫒绫诲瀷', { type: 'error' });
+      return;
+    }
+
+    setLoading(true);
+    let success = 0;
+    let fail = 0;
+    for (const p of toSave) {
+      const body = {
+        stationName: (p.stationName || '').trim(),
+        stationId: (p.stationId || '').trim(),
+        type: common.type,
+        useStatus: common.useStatus || undefined,
+        areaIds: common.areaIds,
+        containerTypes: common.containerTypes,
+        inAble: common.inAble,
+        outAble: common.outAble,
+      };
+      try {
+        const res = await request.post('/basStation/save', body);
+        if (res?.data?.code === 200) success++;
+        else {
+          fail++;
+          notify(res?.data?.msg || '淇濆瓨澶辫触', { type: 'error' });
+        }
+      } catch (e) {
+        fail++;
+        notify(e?.message || '淇濆瓨澶辫触', { type: 'error' });
+      }
+    }
+    setLoading(false);
+    if (success > 0) {
+      notify(`鎴愬姛淇濆瓨 ${success} 鏉�${fail > 0 ? `锛屽け璐� ${fail} 鏉 : ''}`);
+      setOpen(false);
+      refresh();
+    }
+  };
+
+  return (
+    <Dialog
+      open={open}
+      onClose={handleClose}
+      aria-labelledby="form-dialog-title"
+      fullWidth
+      disableRestoreFocus
+      maxWidth="md"
+    >
+      <DialogTitle
+        id="form-dialog-title"
+        sx={{
+          position: 'sticky',
+          top: 0,
+          backgroundColor: 'background.paper',
+          zIndex: 1000,
+        }}
+      >
+        {translate('toolbar.siteInit')}
+        <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+          <DialogCloseButton onClose={handleClose} />
+        </Box>
+      </DialogTitle>
+      <DialogContent sx={{ mt: 2 }}>
+        <Grid container rowSpacing={2} columnSpacing={2}>
+          <Grid item xs={12}>
+            <TableContainer component={Paper} variant="outlined">
+              <Table size="small">
+                <TableHead>
+                  <TableRow>
+                    <TableCell>{translate('table.field.basStation.stationName')}</TableCell>
+                    <TableCell>{translate('table.field.basStation.stationId')}</TableCell>
+                    <TableCell width={80}>鎿嶄綔</TableCell>
+                  </TableRow>
+                </TableHead>
+                <TableBody>
+                  {pairs.map((p, index) => (
+                    <TableRow key={index}>
+                      <TableCell>
+                        <TextField
+                          size="small"
+                          fullWidth
+                          placeholder={translate('table.field.basStation.stationName')}
+                          value={p.stationName || ''}
+                          onChange={(e) => changePair(index, 'stationName', e.target.value)}
+                        />
+                      </TableCell>
+                      <TableCell>
+                        <TextField
+                          size="small"
+                          fullWidth
+                          placeholder={translate('table.field.basStation.stationId')}
+                          value={p.stationId || ''}
+                          onChange={(e) => changePair(index, 'stationId', e.target.value)}
+                        />
+                      </TableCell>
+                      <TableCell>
+                        <IconButton
+                          size="small"
+                          onClick={() => removeRow(index)}
+                          disabled={pairs.length <= 1}
+                        >
+                          <DeleteIcon fontSize="small" />
+                        </IconButton>
+                      </TableCell>
+                    </TableRow>
+                  ))}
+                </TableBody>
+              </Table>
+            </TableContainer>
+            <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
+              <span>{translate('table.field.basStation.stationName')} / {translate('table.field.basStation.stationId')}锛堝繀濉級</span>
+              <Button size="small" startIcon={<AddIcon />} onClick={addRow}>
+                鏂板涓�琛�
+              </Button>
+            </Box>
+
+          </Grid>
+
+          {/* 浠ヤ笅涓庢柊澧為〉椤哄簭銆佸惈涔夊畬鍏ㄤ竴鑷� */}
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.type')}</InputLabel>
+              <Select
+                label={translate('table.field.basStation.type')}
+                value={common.type}
+                onChange={(e) => setCommon((c) => ({ ...c, type: e.target.value }))}
+              >
+                <MenuItem value={0}>鏅鸿兘绔欑偣</MenuItem>
+                <MenuItem value={1}>鏅�氱珯鐐�</MenuItem>
+              </Select>
+            </FormControl>
+          </Grid>
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.useStatus')}</InputLabel>
+              <Select
+                label={translate('table.field.basStation.useStatus')}
+                value={common.useStatus ?? ''}
+                onChange={(e) => setCommon((c) => ({ ...c, useStatus: e.target.value }))}
+              >
+                <MenuItem value="">璇烽�夋嫨</MenuItem>
+                {useStatusOptions.map((opt) => (
+                  <MenuItem key={opt.value} value={opt.value}>
+                    {opt.label}
+                  </MenuItem>
+                ))}
+              </Select>
+            </FormControl>
+          </Grid>
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.crossZoneArea')}</InputLabel>
+              <Select
+                multiple
+                label={translate('table.field.basStation.crossZoneArea')}
+                value={common.areaIds ?? []}
+                onChange={(e) => setCommon((c) => ({ ...c, areaIds: e.target.value }))}
+                renderValue={(selected) =>
+                  (selected || [])
+                    .map((id) => areaOptions.find((a) => a.id === id)?.name ?? id)
+                    .join(', ')
+                }
+              >
+                {areaOptions.map((opt) => (
+                  <MenuItem key={opt.id} value={opt.id}>
+                    {opt.name}
+                  </MenuItem>
+                ))}
+              </Select>
+            </FormControl>
+          </Grid>
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.containerType')}</InputLabel>
+              <Select
+                multiple
+                label={translate('table.field.basStation.containerType')}
+                value={common.containerTypes ?? []}
+                onChange={(e) => setCommon((c) => ({ ...c, containerTypes: e.target.value }))}
+                renderValue={(selected) =>
+                  (selected || [])
+                    .map((id) => containerTypeOptions.find((c) => c.id === id)?.label ?? id)
+                    .join(', ')
+                }
+              >
+                {containerTypeOptions.map((opt) => (
+                  <MenuItem key={opt.id} value={opt.id}>
+                    {opt.label}
+                  </MenuItem>
+                ))}
+              </Select>
+            </FormControl>
+          </Grid>
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.inAble')}</InputLabel>
+              <Select
+                label={translate('table.field.basStation.inAble')}
+                value={common.inAble}
+                onChange={(e) => setCommon((c) => ({ ...c, inAble: e.target.value }))}
+              >
+                <MenuItem value={0}>鍚�</MenuItem>
+                <MenuItem value={1}>鏄�</MenuItem>
+              </Select>
+            </FormControl>
+          </Grid>
+          <Grid item xs={6} display="flex" gap={1}>
+            <FormControl fullWidth size="small">
+              <InputLabel>{translate('table.field.basStation.outAble')}</InputLabel>
+              <Select
+                label={translate('table.field.basStation.outAble')}
+                value={common.outAble}
+                onChange={(e) => setCommon((c) => ({ ...c, outAble: e.target.value }))}
+              >
+                <MenuItem value={0}>鍚�</MenuItem>
+                <MenuItem value={1}>鏄�</MenuItem>
+              </Select>
+            </FormControl>
+          </Grid>
+        </Grid>
+      </DialogContent>
+      <DialogActions
+        sx={{
+          position: 'sticky',
+          bottom: 0,
+          backgroundColor: 'background.paper',
+          zIndex: 1000,
+        }}
+      >
+        <Button
+          disabled={loading}
+          variant="contained"
+          startIcon={<SaveIcon />}
+          onClick={handleSubmit}
+        >
+          {loading ? '淇濆瓨涓�...' : translate('ra.action.save')}
+        </Button>
+      </DialogActions>
+    </Dialog>
+  );
+};
+
+export default BasStationInitModal;
\ No newline at end of file
diff --git a/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx b/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
index eb4489a..49be844 100644
--- a/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
+++ b/rsf-admin/src/page/basicInfo/basStation/BasStationList.jsx
@@ -35,8 +35,10 @@
 } from 'react-admin';
 import { Box, Typography, Card, Stack, Button } from '@mui/material';
 import ContentCopyIcon from '@mui/icons-material/ContentCopy';
+import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
 import { styled } from '@mui/material/styles';
 import BasStationCreate from "./BasStationCreate";
+import BasStationInitModal from "./BasStationInitModal";
 import BasStationPanel from "./BasStationPanel";
 import EmptyData from "../../components/EmptyData";
 import MyCreateButton from "../../components/MyCreateButton";
@@ -125,6 +127,7 @@
     const [areaFieldDialog, setAreaFieldDialog] = useState(false);
     const [areaFieldDialog2, setAreaFieldDialog2] = useState(false);
     const [copyRecord, setCopyRecord] = useState(null);
+    const [initDialogOpen, setInitDialogOpen] = useState(false);
     return (
         <Box display="flex">
             <List
@@ -144,6 +147,16 @@
                     <TopToolbar>
                         <FilterButton />
                         <MyCreateButton onClick={() => { setCreateDialog(true) }} />
+                        <Button
+                          variant="text"
+                          color="primary"
+                          size="small"
+                          startIcon={<PlaylistAddIcon />}
+                          onClick={() => setInitDialogOpen(true)}
+                          sx={{ ml: 1 }}
+                        >
+                          {translate('toolbar.siteInit')}
+                        </Button>
                         <SelectColumnsButton preferenceKey='basStation' />
                         <MyExportButton />
                     </TopToolbar>
@@ -238,7 +251,10 @@
                 copyRecord={copyRecord}
                 onClose={() => setCopyRecord(null)}
             />
-
+            <BasStationInitModal
+              open={initDialogOpen}
+              setOpen={setInitDialogOpen}
+            />
             <PageDrawer
                 title='BasStation Detail'
                 drawerVal={drawerVal}

--
Gitblit v1.9.1