|
|
@@ -0,0 +1,571 @@ |
|
|
|
|
|
"use client"; |
|
|
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
|
Box, |
|
|
|
|
|
Button, |
|
|
|
|
|
Stack, |
|
|
|
|
|
TextField, |
|
|
|
|
|
Typography, |
|
|
|
|
|
Alert, |
|
|
|
|
|
CircularProgress, |
|
|
|
|
|
Table, |
|
|
|
|
|
TableBody, |
|
|
|
|
|
TableCell, |
|
|
|
|
|
TableContainer, |
|
|
|
|
|
TableHead, |
|
|
|
|
|
TableRow, |
|
|
|
|
|
Paper, |
|
|
|
|
|
TablePagination, |
|
|
|
|
|
Modal, |
|
|
|
|
|
Card, |
|
|
|
|
|
CardContent, |
|
|
|
|
|
CardActions, |
|
|
|
|
|
Chip, |
|
|
|
|
|
Accordion, |
|
|
|
|
|
AccordionSummary, |
|
|
|
|
|
AccordionDetails, |
|
|
|
|
|
Checkbox, |
|
|
|
|
|
} from "@mui/material"; |
|
|
|
|
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; |
|
|
|
|
|
import { useCallback, useEffect, useState, useRef, useMemo } from "react"; |
|
|
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
|
|
import { useRouter } from "next/navigation"; |
|
|
|
|
|
import { |
|
|
|
|
|
fetchCompletedJobOrderPickOrdersrecords, |
|
|
|
|
|
fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick |
|
|
|
|
|
} from "@/app/api/jo/actions"; |
|
|
|
|
|
import { fetchNameList, NameList } from "@/app/api/user/actions"; |
|
|
|
|
|
import { |
|
|
|
|
|
FormProvider, |
|
|
|
|
|
useForm, |
|
|
|
|
|
} from "react-hook-form"; |
|
|
|
|
|
import SearchBox, { Criterion } from "../SearchBox"; |
|
|
|
|
|
import { useSession } from "next-auth/react"; |
|
|
|
|
|
import { SessionWithTokens } from "@/config/authConfig"; |
|
|
|
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
|
|
filterArgs: Record<string, any>; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:已完成的 Job Order Pick Order 接口 |
|
|
|
|
|
interface CompletedJobOrderPickOrder { |
|
|
|
|
|
id: number; |
|
|
|
|
|
pickOrderId: number; |
|
|
|
|
|
pickOrderCode: string; |
|
|
|
|
|
pickOrderConsoCode: string; |
|
|
|
|
|
pickOrderTargetDate: string; |
|
|
|
|
|
pickOrderStatus: string; |
|
|
|
|
|
completedDate: string; |
|
|
|
|
|
jobOrderId: number; |
|
|
|
|
|
jobOrderCode: string; |
|
|
|
|
|
jobOrderName: string; |
|
|
|
|
|
reqQty: number; |
|
|
|
|
|
uom: string; |
|
|
|
|
|
planStart: string; |
|
|
|
|
|
planEnd: string; |
|
|
|
|
|
secondScanCompleted: boolean; |
|
|
|
|
|
totalItems: number; |
|
|
|
|
|
completedItems: number; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 新增:Lot 详情接口 |
|
|
|
|
|
interface LotDetail { |
|
|
|
|
|
lotId: number; |
|
|
|
|
|
lotNo: string; |
|
|
|
|
|
expiryDate: string; |
|
|
|
|
|
location: string; |
|
|
|
|
|
availableQty: number; |
|
|
|
|
|
requiredQty: number; |
|
|
|
|
|
actualPickQty: number; |
|
|
|
|
|
processingStatus: string; |
|
|
|
|
|
lotAvailability: string; |
|
|
|
|
|
pickOrderId: number; |
|
|
|
|
|
pickOrderCode: string; |
|
|
|
|
|
pickOrderConsoCode: string; |
|
|
|
|
|
pickOrderLineId: number; |
|
|
|
|
|
stockOutLineId: number; |
|
|
|
|
|
stockOutLineStatus: string; |
|
|
|
|
|
routerIndex: number; |
|
|
|
|
|
routerArea: string; |
|
|
|
|
|
routerRoute: string; |
|
|
|
|
|
uomShortDesc: string; |
|
|
|
|
|
secondQrScanStatus: string; |
|
|
|
|
|
itemId: number; |
|
|
|
|
|
itemCode: string; |
|
|
|
|
|
itemName: string; |
|
|
|
|
|
uomCode: string; |
|
|
|
|
|
uomDesc: string; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const CompleteJobOrderRecord: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
|
|
const { t } = useTranslation("jo"); |
|
|
|
|
|
const router = useRouter(); |
|
|
|
|
|
const { data: session } = useSession() as { data: SessionWithTokens | null }; |
|
|
|
|
|
|
|
|
|
|
|
const currentUserId = session?.id ? parseInt(session.id) : undefined; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:已完成 Job Order Pick Orders 状态 |
|
|
|
|
|
const [completedJobOrderPickOrders, setCompletedJobOrderPickOrders] = useState<CompletedJobOrderPickOrder[]>([]); |
|
|
|
|
|
const [completedJobOrderPickOrdersLoading, setCompletedJobOrderPickOrdersLoading] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:详情视图状态 |
|
|
|
|
|
const [selectedJobOrderPickOrder, setSelectedJobOrderPickOrder] = useState<CompletedJobOrderPickOrder | null>(null); |
|
|
|
|
|
const [showDetailView, setShowDetailView] = useState(false); |
|
|
|
|
|
const [detailLotData, setDetailLotData] = useState<LotDetail[]>([]); |
|
|
|
|
|
const [detailLotDataLoading, setDetailLotDataLoading] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:搜索状态 |
|
|
|
|
|
const [searchQuery, setSearchQuery] = useState<Record<string, any>>({}); |
|
|
|
|
|
const [filteredJobOrderPickOrders, setFilteredJobOrderPickOrders] = useState<CompletedJobOrderPickOrder[]>([]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:分页状态 |
|
|
|
|
|
const [paginationController, setPaginationController] = useState({ |
|
|
|
|
|
pageNum: 0, |
|
|
|
|
|
pageSize: 10, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const formProps = useForm(); |
|
|
|
|
|
const errors = formProps.formState.errors; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:使用新的 Job Order API 获取已完成的 Job Order Pick Orders(仅完成pick的) |
|
|
|
|
|
const fetchCompletedJobOrderPickOrdersData = useCallback(async () => { |
|
|
|
|
|
if (!currentUserId) return; |
|
|
|
|
|
|
|
|
|
|
|
setCompletedJobOrderPickOrdersLoading(true); |
|
|
|
|
|
try { |
|
|
|
|
|
console.log("🔍 Fetching completed Job Order pick orders (pick completed only)..."); |
|
|
|
|
|
|
|
|
|
|
|
const completedJobOrderPickOrders = await fetchCompletedJobOrderPickOrdersrecords(currentUserId); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Fix: Ensure the data is always an array |
|
|
|
|
|
const safeData = Array.isArray(completedJobOrderPickOrders) ? completedJobOrderPickOrders : []; |
|
|
|
|
|
|
|
|
|
|
|
setCompletedJobOrderPickOrders(safeData); |
|
|
|
|
|
setFilteredJobOrderPickOrders(safeData); |
|
|
|
|
|
console.log("✅ Fetched completed Job Order pick orders:", safeData); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error("❌ Error fetching completed Job Order pick orders:", error); |
|
|
|
|
|
setCompletedJobOrderPickOrders([]); |
|
|
|
|
|
setFilteredJobOrderPickOrders([]); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setCompletedJobOrderPickOrdersLoading(false); |
|
|
|
|
|
} |
|
|
|
|
|
}, [currentUserId]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 新增:获取 lot 详情数据(使用新的API) |
|
|
|
|
|
const fetchLotDetailsData = useCallback(async (pickOrderId: number) => { |
|
|
|
|
|
setDetailLotDataLoading(true); |
|
|
|
|
|
try { |
|
|
|
|
|
console.log("🔍 Fetching lot details for completed pick order:", pickOrderId); |
|
|
|
|
|
|
|
|
|
|
|
const lotDetails = await fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId); |
|
|
|
|
|
|
|
|
|
|
|
setDetailLotData(lotDetails); |
|
|
|
|
|
console.log("✅ Fetched lot details:", lotDetails); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error("❌ Error fetching lot details:", error); |
|
|
|
|
|
setDetailLotData([]); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setDetailLotDataLoading(false); |
|
|
|
|
|
} |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:初始化时获取数据 |
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (currentUserId) { |
|
|
|
|
|
fetchCompletedJobOrderPickOrdersData(); |
|
|
|
|
|
} |
|
|
|
|
|
}, [currentUserId, fetchCompletedJobOrderPickOrdersData]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:搜索功能 |
|
|
|
|
|
const handleSearch = useCallback((query: Record<string, any>) => { |
|
|
|
|
|
setSearchQuery({ ...query }); |
|
|
|
|
|
console.log("Search query:", query); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Fix: Ensure completedJobOrderPickOrders is an array before filtering |
|
|
|
|
|
if (!Array.isArray(completedJobOrderPickOrders)) { |
|
|
|
|
|
setFilteredJobOrderPickOrders([]); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const filtered = completedJobOrderPickOrders.filter((pickOrder) => { |
|
|
|
|
|
const pickOrderCodeMatch = !query.pickOrderCode || |
|
|
|
|
|
pickOrder.pickOrderCode?.toLowerCase().includes((query.pickOrderCode || "").toLowerCase()); |
|
|
|
|
|
|
|
|
|
|
|
const jobOrderCodeMatch = !query.jobOrderCode || |
|
|
|
|
|
pickOrder.jobOrderCode?.toLowerCase().includes((query.jobOrderCode || "").toLowerCase()); |
|
|
|
|
|
|
|
|
|
|
|
const jobOrderNameMatch = !query.jobOrderName || |
|
|
|
|
|
pickOrder.jobOrderName?.toLowerCase().includes((query.jobOrderName || "").toLowerCase()); |
|
|
|
|
|
|
|
|
|
|
|
return pickOrderCodeMatch && jobOrderCodeMatch && jobOrderNameMatch; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
setFilteredJobOrderPickOrders(filtered); |
|
|
|
|
|
console.log("Filtered Job Order pick orders count:", filtered.length); |
|
|
|
|
|
}, [completedJobOrderPickOrders]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:重置搜索 |
|
|
|
|
|
const handleSearchReset = useCallback(() => { |
|
|
|
|
|
setSearchQuery({}); |
|
|
|
|
|
// ✅ Fix: Ensure completedJobOrderPickOrders is an array before setting |
|
|
|
|
|
setFilteredJobOrderPickOrders(Array.isArray(completedJobOrderPickOrders) ? completedJobOrderPickOrders : []); |
|
|
|
|
|
}, [completedJobOrderPickOrders]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:分页功能 |
|
|
|
|
|
const handlePageChange = useCallback((event: unknown, newPage: number) => { |
|
|
|
|
|
setPaginationController(prev => ({ |
|
|
|
|
|
...prev, |
|
|
|
|
|
pageNum: newPage, |
|
|
|
|
|
})); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
const handlePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => { |
|
|
|
|
|
const newPageSize = parseInt(event.target.value, 10); |
|
|
|
|
|
setPaginationController({ |
|
|
|
|
|
pageNum: 0, |
|
|
|
|
|
pageSize: newPageSize, |
|
|
|
|
|
}); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:分页数据 |
|
|
|
|
|
const paginatedData = useMemo(() => { |
|
|
|
|
|
// ✅ Fix: Ensure filteredJobOrderPickOrders is an array before calling slice |
|
|
|
|
|
if (!Array.isArray(filteredJobOrderPickOrders)) { |
|
|
|
|
|
return []; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const startIndex = paginationController.pageNum * paginationController.pageSize; |
|
|
|
|
|
const endIndex = startIndex + paginationController.pageSize; |
|
|
|
|
|
return filteredJobOrderPickOrders.slice(startIndex, endIndex); |
|
|
|
|
|
}, [filteredJobOrderPickOrders, paginationController]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:搜索条件 |
|
|
|
|
|
const searchCriteria: Criterion<any>[] = [ |
|
|
|
|
|
{ |
|
|
|
|
|
label: t("Pick Order Code"), |
|
|
|
|
|
paramName: "pickOrderCode", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
label: t("Job Order Code"), |
|
|
|
|
|
paramName: "jobOrderCode", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
label: t("Job Order Item Name"), |
|
|
|
|
|
paramName: "jobOrderName", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
} |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:详情点击处理 |
|
|
|
|
|
const handleDetailClick = useCallback(async (jobOrderPickOrder: CompletedJobOrderPickOrder) => { |
|
|
|
|
|
setSelectedJobOrderPickOrder(jobOrderPickOrder); |
|
|
|
|
|
setShowDetailView(true); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 获取 lot 详情数据(使用新的API) |
|
|
|
|
|
await fetchLotDetailsData(jobOrderPickOrder.pickOrderId); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 触发打印按钮状态更新 - 基于详情数据 |
|
|
|
|
|
const allCompleted = jobOrderPickOrder.secondScanCompleted; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 发送事件,包含标签页信息 |
|
|
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { |
|
|
|
|
|
detail: { |
|
|
|
|
|
allLotsCompleted: allCompleted, |
|
|
|
|
|
tabIndex: 3 // ✅ 明确指定这是来自标签页 3 的事件 |
|
|
|
|
|
} |
|
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
}, [fetchLotDetailsData]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:返回列表视图 |
|
|
|
|
|
const handleBackToList = useCallback(() => { |
|
|
|
|
|
setShowDetailView(false); |
|
|
|
|
|
setSelectedJobOrderPickOrder(null); |
|
|
|
|
|
setDetailLotData([]); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 返回列表时禁用打印按钮 |
|
|
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { |
|
|
|
|
|
detail: { |
|
|
|
|
|
allLotsCompleted: false, |
|
|
|
|
|
tabIndex: 3 |
|
|
|
|
|
} |
|
|
|
|
|
})); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:如果显示详情视图,渲染 Job Order 详情和 Lot 信息 |
|
|
|
|
|
if (showDetailView && selectedJobOrderPickOrder) { |
|
|
|
|
|
return ( |
|
|
|
|
|
<FormProvider {...formProps}> |
|
|
|
|
|
<Box> |
|
|
|
|
|
{/* 返回按钮和标题 */} |
|
|
|
|
|
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}> |
|
|
|
|
|
<Button variant="outlined" onClick={handleBackToList}> |
|
|
|
|
|
{t("Back to List")} |
|
|
|
|
|
</Button> |
|
|
|
|
|
<Typography variant="h6"> |
|
|
|
|
|
{t("Job Order Pick Order Details")}: {selectedJobOrderPickOrder.pickOrderCode} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{/* Job Order 信息卡片 */} |
|
|
|
|
|
<Card sx={{ mb: 2 }}> |
|
|
|
|
|
<CardContent> |
|
|
|
|
|
<Stack direction="row" spacing={4} useFlexGap flexWrap="wrap"> |
|
|
|
|
|
<Typography variant="subtitle1"> |
|
|
|
|
|
<strong>{t("Pick Order Code")}:</strong> {selectedJobOrderPickOrder.pickOrderCode} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="subtitle1"> |
|
|
|
|
|
<strong>{t("Job Order Code")}:</strong> {selectedJobOrderPickOrder.jobOrderCode} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="subtitle1"> |
|
|
|
|
|
<strong>{t("Job Order Item Name")}:</strong> {selectedJobOrderPickOrder.jobOrderName} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="subtitle1"> |
|
|
|
|
|
<strong>{t("Target Date")}:</strong> {selectedJobOrderPickOrder.pickOrderTargetDate} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
|
|
|
|
|
|
<Stack direction="row" spacing={4} useFlexGap flexWrap="wrap" sx={{ mt: 2 }}> |
|
|
|
|
|
<Typography variant="subtitle1"> |
|
|
|
|
|
<strong>{t("Required Qty")}:</strong> {selectedJobOrderPickOrder.reqQty} {selectedJobOrderPickOrder.uom} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
</Stack> |
|
|
|
|
|
</CardContent> |
|
|
|
|
|
</Card> |
|
|
|
|
|
|
|
|
|
|
|
{/* ✅ 修改:Lot 详情表格 - 添加复选框列 */} |
|
|
|
|
|
<Card> |
|
|
|
|
|
<CardContent> |
|
|
|
|
|
<Typography variant="h6" gutterBottom> |
|
|
|
|
|
{t("Lot Details")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
{detailLotDataLoading ? ( |
|
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 2 }}> |
|
|
|
|
|
<CircularProgress /> |
|
|
|
|
|
</Box> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<TableContainer component={Paper}> |
|
|
|
|
|
<Table> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>{t("Index")}</TableCell> |
|
|
|
|
|
<TableCell>{t("Route")}</TableCell> |
|
|
|
|
|
<TableCell>{t("Item Code")}</TableCell> |
|
|
|
|
|
<TableCell>{t("Item Name")}</TableCell> |
|
|
|
|
|
<TableCell>{t("Lot No")}</TableCell> |
|
|
|
|
|
<TableCell>{t("Location")}</TableCell> |
|
|
|
|
|
<TableCell align="right">{t("Required Qty")}</TableCell> |
|
|
|
|
|
<TableCell align="right">{t("Actual Pick Qty")}</TableCell> |
|
|
|
|
|
<TableCell align="center">{t("Processing Status")}</TableCell> |
|
|
|
|
|
<TableCell align="center">{t("Second Scan Status")}</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{detailLotData.length === 0 ? ( |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell colSpan={10} align="center"> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{t("No lot details available")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
) : ( |
|
|
|
|
|
detailLotData.map((lot, index) => ( |
|
|
|
|
|
<TableRow key={lot.lotId}> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<Typography variant="body2" fontWeight="bold"> |
|
|
|
|
|
{index + 1} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<Typography variant="body2"> |
|
|
|
|
|
{lot.routerRoute || '-'} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell>{lot.itemCode}</TableCell> |
|
|
|
|
|
<TableCell>{lot.itemName}</TableCell> |
|
|
|
|
|
<TableCell>{lot.lotNo}</TableCell> |
|
|
|
|
|
<TableCell>{lot.location}</TableCell> |
|
|
|
|
|
<TableCell align="right"> |
|
|
|
|
|
{lot.requiredQty?.toLocaleString() || 0} ({lot.uomShortDesc}) |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell align="right"> |
|
|
|
|
|
{lot.actualPickQty?.toLocaleString() || 0} ({lot.uomShortDesc}) |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
{/* ✅ 修改:Processing Status 使用复选框 */} |
|
|
|
|
|
<TableCell align="center"> |
|
|
|
|
|
<Box sx={{ |
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
width: '100%', |
|
|
|
|
|
height: '100%' |
|
|
|
|
|
}}> |
|
|
|
|
|
<Checkbox |
|
|
|
|
|
checked={lot.processingStatus === 'completed'} |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
readOnly={true} |
|
|
|
|
|
size="large" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
color: lot.processingStatus === 'completed' ? 'success.main' : 'grey.400', |
|
|
|
|
|
'&.Mui-checked': { |
|
|
|
|
|
color: 'success.main', |
|
|
|
|
|
}, |
|
|
|
|
|
transform: 'scale(1.3)', |
|
|
|
|
|
'& .MuiSvgIcon-root': { |
|
|
|
|
|
fontSize: '1.5rem', |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
{/* ✅ 修改:Second Scan Status 使用复选框 */} |
|
|
|
|
|
<TableCell align="center"> |
|
|
|
|
|
<Box sx={{ |
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
width: '100%', |
|
|
|
|
|
height: '100%' |
|
|
|
|
|
}}> |
|
|
|
|
|
<Checkbox |
|
|
|
|
|
checked={lot.secondQrScanStatus === 'completed'} |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
readOnly={true} |
|
|
|
|
|
size="large" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
color: lot.secondQrScanStatus === 'completed' ? 'success.main' : 'grey.400', |
|
|
|
|
|
'&.Mui-checked': { |
|
|
|
|
|
color: 'success.main', |
|
|
|
|
|
}, |
|
|
|
|
|
transform: 'scale(1.3)', |
|
|
|
|
|
'& .MuiSvgIcon-root': { |
|
|
|
|
|
fontSize: '1.5rem', |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
)) |
|
|
|
|
|
)} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
)} |
|
|
|
|
|
</CardContent> |
|
|
|
|
|
</Card> |
|
|
|
|
|
</Box> |
|
|
|
|
|
</FormProvider> |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修改:默认列表视图 |
|
|
|
|
|
return ( |
|
|
|
|
|
<FormProvider {...formProps}> |
|
|
|
|
|
<Box> |
|
|
|
|
|
{/* 搜索框 */} |
|
|
|
|
|
<Box sx={{ mb: 2 }}> |
|
|
|
|
|
<SearchBox |
|
|
|
|
|
criteria={searchCriteria} |
|
|
|
|
|
onSearch={handleSearch} |
|
|
|
|
|
onReset={handleSearchReset} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{/* 加载状态 */} |
|
|
|
|
|
{completedJobOrderPickOrdersLoading ? ( |
|
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}> |
|
|
|
|
|
<CircularProgress /> |
|
|
|
|
|
</Box> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<Box> |
|
|
|
|
|
{/* 结果统计 */} |
|
|
|
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}> |
|
|
|
|
|
{t("Total")}: {filteredJobOrderPickOrders.length} {t("completed Job Order pick orders with matching")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
{/* 列表 */} |
|
|
|
|
|
{filteredJobOrderPickOrders.length === 0 ? ( |
|
|
|
|
|
<Box sx={{ p: 3, textAlign: 'center' }}> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{t("No completed Job Order pick orders with matching found")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Box> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<Stack spacing={2}> |
|
|
|
|
|
{paginatedData.map((jobOrderPickOrder) => ( |
|
|
|
|
|
<Card key={jobOrderPickOrder.id}> |
|
|
|
|
|
<CardContent> |
|
|
|
|
|
<Stack direction="row" justifyContent="space-between" alignItems="center"> |
|
|
|
|
|
<Box> |
|
|
|
|
|
<Typography variant="h6"> |
|
|
|
|
|
{jobOrderPickOrder.pickOrderCode} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{jobOrderPickOrder.jobOrderName} - {jobOrderPickOrder.jobOrderCode} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{t("Completed")}: {new Date(jobOrderPickOrder.completedDate).toLocaleString()} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{t("Target Date")}: {jobOrderPickOrder.pickOrderTargetDate} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Box> |
|
|
|
|
|
<Box> |
|
|
|
|
|
<Chip |
|
|
|
|
|
label={jobOrderPickOrder.pickOrderStatus} |
|
|
|
|
|
color={jobOrderPickOrder.pickOrderStatus === 'completed' ? 'success' : 'default'} |
|
|
|
|
|
size="small" |
|
|
|
|
|
sx={{ mb: 1 }} |
|
|
|
|
|
/> |
|
|
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
|
|
{jobOrderPickOrder.completedItems}/{jobOrderPickOrder.totalItems} {t("items completed")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Chip |
|
|
|
|
|
label={jobOrderPickOrder.secondScanCompleted ? t("Second Scan Completed") : t("Second Scan Pending")} |
|
|
|
|
|
color={jobOrderPickOrder.secondScanCompleted ? 'success' : 'warning'} |
|
|
|
|
|
size="small" |
|
|
|
|
|
sx={{ mt: 1 }} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</CardContent> |
|
|
|
|
|
<CardActions> |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
onClick={() => handleDetailClick(jobOrderPickOrder)} |
|
|
|
|
|
> |
|
|
|
|
|
{t("View Details")} |
|
|
|
|
|
</Button> |
|
|
|
|
|
</CardActions> |
|
|
|
|
|
</Card> |
|
|
|
|
|
))} |
|
|
|
|
|
</Stack> |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
{/* 分页 */} |
|
|
|
|
|
{filteredJobOrderPickOrders.length > 0 && ( |
|
|
|
|
|
<TablePagination |
|
|
|
|
|
component="div" |
|
|
|
|
|
count={filteredJobOrderPickOrders.length} |
|
|
|
|
|
page={paginationController.pageNum} |
|
|
|
|
|
rowsPerPage={paginationController.pageSize} |
|
|
|
|
|
onPageChange={handlePageChange} |
|
|
|
|
|
onRowsPerPageChange={handlePageSizeChange} |
|
|
|
|
|
rowsPerPageOptions={[5, 10, 25, 50]} |
|
|
|
|
|
/> |
|
|
|
|
|
)} |
|
|
|
|
|
</Box> |
|
|
|
|
|
)} |
|
|
|
|
|
</Box> |
|
|
|
|
|
</FormProvider> |
|
|
|
|
|
); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export default CompleteJobOrderRecord; |