From 1bc33546a044cbc84dd9595c19dbcd9a4e309fc9 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期六, 10 一月 2026 14:06:10 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/page/sta/StaPanel.jsx | 205 ++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 162 insertions(+), 43 deletions(-)
diff --git a/zy-acs-flow/src/page/sta/StaPanel.jsx b/zy-acs-flow/src/page/sta/StaPanel.jsx
index 1e8414a..b247569 100644
--- a/zy-acs-flow/src/page/sta/StaPanel.jsx
+++ b/zy-acs-flow/src/page/sta/StaPanel.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useState, useEffect, useMemo } from "react";
import {
Box,
Card,
@@ -13,11 +13,16 @@
Divider,
CircularProgress,
Stack,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
} from '@mui/material';
import {
useTranslate,
useRecordContext,
useDataProvider,
+ useGetMany,
} from 'react-admin';
import { format } from 'date-fns';
import * as Common from '@/utils/common'
@@ -30,23 +35,60 @@
];
const INFO_FIELDS = [
- { labelKey: 'table.field.sta.uuid', valueKey: 'uuid' },
{ labelKey: 'table.field.sta.zoneId', valueKey: 'zoneId$' },
{ labelKey: 'table.field.sta.staType', valueKey: 'staType$' },
{ labelKey: 'table.field.sta.code', valueKey: 'code$' },
{ labelKey: 'table.field.sta.capacity', valueKey: 'capacity' },
- { labelKey: 'table.field.sta.offset', valueKey: 'offset' },
{ labelKey: 'table.field.sta.rsvInCnt', valueKey: 'rsvInCnt' },
{ labelKey: 'table.field.sta.rsvOutCnt', valueKey: 'rsvOutCnt' },
- { labelKey: 'table.field.sta.staSts', valueKey: 'staSts$' },
+ { labelKey: 'table.field.sta.offset', valueKey: 'offset' },
{ labelKey: 'table.field.sta.zpallet', valueKey: 'zpallet' },
];
+
+const RESERVE_COLUMN_COUNT = 11;
+const RESERVE_TYPE_OPTIONS = ['IN', 'OUT'];
+const RESERVE_STATE_OPTIONS = ['reserved', 'waiting', 'confirmed', 'canceled', 'timeout'];
const StaPanel = () => {
const record = useRecordContext();
const translate = useTranslate();
const dataProvider = useDataProvider();
const [reserves, setReserves] = useState([]);
const [isReservesLoading, setIsReservesLoading] = useState(false);
+ const [typeFilter, setTypeFilter] = useState('');
+ const [stateFilter, setStateFilter] = useState('');
+ const taskIds = useMemo(() => extractIds(reserves, 'taskId'), [reserves]);
+ const segmentIds = useMemo(() => extractIds(reserves, 'segmentId'), [reserves]);
+ const agvIds = useMemo(() => extractIds(reserves, 'agvId'), [reserves]);
+
+ const { data: taskRecords = [] } = useGetMany('task', { ids: taskIds }, { enabled: taskIds.length > 0 });
+ const { data: segmentRecords = [] } = useGetMany('segment', { ids: segmentIds }, { enabled: segmentIds.length > 0 });
+ const { data: agvRecords = [] } = useGetMany('agv', { ids: agvIds }, { enabled: agvIds.length > 0 });
+
+ const taskLabelMap = useMemo(
+ () => createLabelMap(taskRecords, (item) => item.seqNum),
+ [taskRecords]
+ );
+ const segmentLabelMap = useMemo(
+ () => createLabelMap(segmentRecords, (item) => item.groupId + '-' + item.serial),
+ [segmentRecords]
+ );
+ const agvLabelMap = useMemo(
+ () => createLabelMap(agvRecords, (item) => item.uuid),
+ [agvRecords]
+ );
+
+ const filteredReserves = useMemo(() => {
+ if (!typeFilter && !stateFilter) {
+ return reserves;
+ }
+ return reserves.filter((reserve) => {
+ const matchesType =
+ !typeFilter || normalizeValueKey(reserve?.type) === normalizeValueKey(typeFilter);
+ const matchesState =
+ !stateFilter || normalizeValueKey(reserve?.state) === normalizeValueKey(stateFilter);
+ return matchesType && matchesState;
+ });
+ }, [reserves, typeFilter, stateFilter]);
useEffect(() => {
if (!record?.id) {
@@ -59,21 +101,18 @@
setIsReservesLoading(true);
dataProvider.getList('staReserve', {
pagination: { page: 1, perPage: 10 },
- sort: { field: 'updateTime', order: 'DESC' },
+ sort: { field: 'updateTime', order: 'desc' },
filter: { staId: record.id },
- })
- .then(({ data }) => {
- if (!isMounted) return;
- setReserves(data || []);
- })
- .catch(() => {
- if (!isMounted) return;
- setReserves([]);
- })
- .finally(() => {
- if (!isMounted) return;
- setIsReservesLoading(false);
- });
+ }).then(({ data }) => {
+ if (!isMounted) return;
+ setReserves(data || []);
+ }).catch(() => {
+ if (!isMounted) return;
+ setReserves([]);
+ }).finally(() => {
+ if (!isMounted) return;
+ setIsReservesLoading(false);
+ });
return () => {
isMounted = false;
@@ -84,11 +123,11 @@
return (
<>
- <Card sx={{ width: { xs: 320, sm: 560, md: 680, lg: 900 }, margin: 'auto', mt: .5, mb: .5 }}>
+ <Card sx={{ maxWidth: '80%', margin: 'auto', mt: .5, mb: .5 }}>
<CardContent>
<Grid container spacing={2}>
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
- <Typography variant="h6" gutterBottom align="left" sx={{
+ <Typography variant="subtitle2" gutterBottom align="left" sx={{
maxWidth: { xs: '140px', sm: '220px', md: '300px', lg: '360px' },
whiteSpace: 'nowrap',
overflow: 'hidden',
@@ -96,19 +135,14 @@
}}>
{Common.camelToPascalWithSpaces(translate('table.field.sta.staNo'))}: {record.staNo}
</Typography>
- <Typography variant="h6" gutterBottom align="right" >
- ID: {record.id}
- </Typography>
- </Grid>
- </Grid>
- <Grid container spacing={2}>
- <Grid item xs={12} container alignContent="flex-end">
<Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
{Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo || '-'}
</Typography>
</Grid>
</Grid>
- <Box height={16}> </Box>
+
+ <Box height={12}> </Box>
+
<Grid container spacing={2}>
{INFO_FIELDS.map(({ labelKey, valueKey }) => (
<Grid item xs={12} sm={6} md={4} key={labelKey}>
@@ -122,9 +156,9 @@
<Divider sx={{ my: 2 }} />
- <Typography variant="subtitle2" color="textSecondary" gutterBottom>
- {translate('common.field.status')} / {translate('table.field.sta.staNo')}
- </Typography>
+ {/* <Typography variant="subtitle2" color="textSecondary" gutterBottom>
+ {translate('common.field.status')}
+ </Typography> */}
<Grid container spacing={2}>
{STATUS_FIELDS.map(({ key, labelKey }) => (
<Grid item xs={6} sm={3} key={key}>
@@ -132,29 +166,74 @@
</Grid>
))}
</Grid>
-
<Divider sx={{ my: 2 }} />
<Box>
<Typography variant="subtitle1" gutterBottom>
{translate('menu.staReserve')}
</Typography>
+ <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} mb={2}>
+ <FormControl size="small" sx={{ minWidth: 160 }}>
+ <InputLabel id="sta-panel-reserve-type-label">
+ {translate('table.field.staReserve.type')}
+ </InputLabel>
+ <Select
+ labelId="sta-panel-reserve-type-label"
+ value={typeFilter}
+ label={translate('table.field.staReserve.type')}
+ onChange={(event) => setTypeFilter(event.target.value)}
+ >
+ <MenuItem value="">
+ {translate('common.action.deselect')}
+ </MenuItem>
+ {RESERVE_TYPE_OPTIONS.map((option) => (
+ <MenuItem key={option} value={option}>
+ {formatReserveType(option, translate)}
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <FormControl size="small" sx={{ minWidth: 160 }}>
+ <InputLabel id="sta-panel-reserve-state-label">
+ {translate('table.field.staReserve.state')}
+ </InputLabel>
+ <Select
+ labelId="sta-panel-reserve-state-label"
+ value={stateFilter}
+ label={translate('table.field.staReserve.state')}
+ onChange={(event) => setStateFilter(event.target.value)}
+ >
+ <MenuItem value="">
+ {translate('common.action.deselect')}
+ </MenuItem>
+ {RESERVE_STATE_OPTIONS.map((option) => (
+ <MenuItem key={option} value={option}>
+ {formatReserveState(option, translate)}
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ </Stack>
<Table size="small">
<TableHead>
<TableRow>
- <TableCell>{translate('table.field.staReserve.name')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.uuid')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.taskId')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.segmentId')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.agvId')}</TableCell>
<TableCell>{translate('table.field.staReserve.type')}</TableCell>
<TableCellRight>{translate('table.field.staReserve.qty')}</TableCellRight>
<TableCell>{translate('table.field.staReserve.state')}</TableCell>
- <TableCell>{translate('table.field.staReserve.agvId')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.expireTime')}</TableCell>
<TableCell>{translate('table.field.staReserve.waitingAt')}</TableCell>
<TableCell>{translate('table.field.staReserve.confirmedAt')}</TableCell>
+ <TableCell>{translate('table.field.staReserve.uniqKey')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{isReservesLoading && (
<TableRow>
- <TableCell colSpan={7}>
+ <TableCell colSpan={RESERVE_COLUMN_COUNT}>
<Box display="flex" alignItems="center" gap={1}>
<CircularProgress size={16} />
<Typography variant="body2" color="textSecondary">
@@ -164,24 +243,28 @@
</TableCell>
</TableRow>
)}
- {!isReservesLoading && reserves.length === 0 && (
+ {!isReservesLoading && filteredReserves.length === 0 && (
<TableRow>
- <TableCell colSpan={7}>
+ <TableCell colSpan={RESERVE_COLUMN_COUNT}>
<Typography variant="body2" color="textSecondary">
{translate('ra.navigation.no_results')}
</Typography>
</TableCell>
</TableRow>
)}
- {reserves.map((reserve) => (
+ {filteredReserves.map((reserve) => (
<TableRow key={reserve.id}>
- <TableCell>{reserve.name || '-'}</TableCell>
+ <TableCell>{reserve.uuid || '-'}</TableCell>
+ <TableCell>{getReferenceLabel(taskLabelMap, reserve.taskId)}</TableCell>
+ <TableCell>{getReferenceLabel(segmentLabelMap, reserve.segmentId)}</TableCell>
+ <TableCell>{getReferenceLabel(agvLabelMap, reserve.agvId)}</TableCell>
<TableCell>{formatReserveType(reserve.type, translate)}</TableCell>
<TableCellRight>{reserve.qty ?? '-'}</TableCellRight>
<TableCell>{formatReserveState(reserve.state, translate)}</TableCell>
- <TableCell>{reserve.agvId$ || reserve.agvId || '-'}</TableCell>
+ <TableCell>{formatDateTime(reserve.expireTime)}</TableCell>
<TableCell>{formatDateTime(reserve.waitingAt)}</TableCell>
<TableCell>{formatDateTime(reserve.confirmedAt)}</TableCell>
+ <TableCell>{reserve.uniqKey || '-'}</TableCell>
</TableRow>
))}
</TableBody>
@@ -200,7 +283,7 @@
<Typography variant="caption" color="textSecondary">
{Common.camelToPascalWithSpaces(translate(labelKey))}
</Typography>
- <Typography variant="body2" fontWeight={600}>
+ <Typography variant="body2" fontWeight={400}>
{formatInfoValue(value)}
</Typography>
</Stack>
@@ -240,7 +323,7 @@
height: 14,
borderRadius: '50%',
backgroundColor: color,
- boxShadow: `0 0 6px ${color}`,
+ boxShadow: `0 0 3px ${color}`,
border: '1px solid rgba(0,0,0,0.12)'
}} />
<Typography variant="body2">
@@ -258,7 +341,7 @@
if (isFalsy(value)) {
return '#9e9e9e';
}
- return '#ff9800';
+ return '#d40000ff';
};
const isTruthy = (value) => {
@@ -288,6 +371,37 @@
return String(value);
};
+const extractIds = (items, key) => {
+ if (!items || items.length === 0) return [];
+ const unique = new Set();
+ items.forEach((item) => {
+ const value = item?.[key];
+ if (value !== undefined && value !== null && value !== '') {
+ unique.add(value);
+ }
+ });
+ return Array.from(unique);
+};
+
+const createLabelMap = (records, getLabel) => {
+ if (!records || records.length === 0) return {};
+ return records.reduce((acc, record) => {
+ if (record?.id === undefined || record?.id === null) {
+ return acc;
+ }
+ const label = getLabel(record);
+ acc[record.id] = label ?? record.id;
+ return acc;
+ }, {});
+};
+
+const getReferenceLabel = (map, id) => {
+ if (id === undefined || id === null || id === '') {
+ return '-';
+ }
+ return map?.[id] || '-';
+};
+
const formatReserveType = (value, translate) =>
formatReserveEnum(value, translate, 'type');
@@ -296,6 +410,11 @@
cancelled: 'canceled',
});
+const getReserveRelationValue = (reserve, key) => {
+ if (!reserve) return '-';
+ return reserve[`${key}$`] || reserve[key] || '-';
+};
+
const formatReserveEnum = (value, translate, enumType, overrides = {}) => {
const normalized = normalizeValueKey(value);
if (!normalized) {
--
Gitblit v1.9.1