FPSMS-frontend
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

723 Zeilen
27 KiB

  1. "use client";
  2. import {
  3. Box,
  4. Button,
  5. Stack,
  6. TextField,
  7. Typography,
  8. Alert,
  9. CircularProgress,
  10. Table,
  11. TableBody,
  12. TableCell,
  13. TableContainer,
  14. TableHead,
  15. TableRow,
  16. Paper,
  17. TablePagination,
  18. Modal,
  19. Card,
  20. CardContent,
  21. CardActions,
  22. Chip,
  23. Accordion,
  24. AccordionSummary,
  25. AccordionDetails,
  26. Checkbox,
  27. Autocomplete,
  28. } from "@mui/material";
  29. import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
  30. import { useCallback, useEffect, useState, useRef, useMemo } from "react";
  31. import { useTranslation } from "react-i18next";
  32. import { useRouter } from "next/navigation";
  33. import {
  34. fetchCompletedJobOrderPickOrdersrecords,
  35. fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick,
  36. PrintPickRecord
  37. } from "@/app/api/jo/actions";
  38. import { fetchNameList, NameList } from "@/app/api/user/actions";
  39. import {
  40. FormProvider,
  41. useForm,
  42. } from "react-hook-form";
  43. import SearchBox, { Criterion } from "../SearchBox";
  44. import { useSession } from "next-auth/react";
  45. import { SessionWithTokens } from "@/config/authConfig";
  46. import Swal from "sweetalert2";
  47. import { PrinterCombo } from "@/app/api/settings/printer";
  48. interface Props {
  49. filterArgs: Record<string, any>;
  50. printerCombo: PrinterCombo[];
  51. }
  52. // 修改:已完成的 Job Order Pick Order 接口
  53. interface CompletedJobOrderPickOrder {
  54. id: number;
  55. pickOrderId: number;
  56. pickOrderCode: string;
  57. pickOrderConsoCode: string;
  58. pickOrderTargetDate: string;
  59. pickOrderStatus: string;
  60. completedDate: string;
  61. jobOrderId: number;
  62. jobOrderCode: string;
  63. jobOrderName: string;
  64. reqQty: number;
  65. uom: string;
  66. planStart: string;
  67. planEnd: string;
  68. secondScanCompleted: boolean;
  69. totalItems: number;
  70. completedItems: number;
  71. }
  72. // 新增:Lot 详情接口
  73. interface LotDetail {
  74. lotId: number;
  75. lotNo: string;
  76. expiryDate: string;
  77. location: string;
  78. availableQty: number;
  79. requiredQty: number;
  80. actualPickQty: number;
  81. processingStatus: string;
  82. lotAvailability: string;
  83. pickOrderId: number;
  84. pickOrderCode: string;
  85. pickOrderConsoCode: string;
  86. pickOrderLineId: number;
  87. stockOutLineId: number;
  88. stockOutLineStatus: string;
  89. routerIndex: number;
  90. routerArea: string;
  91. routerRoute: string;
  92. uomShortDesc: string;
  93. secondQrScanStatus: string;
  94. itemId: number;
  95. itemCode: string;
  96. itemName: string;
  97. uomCode: string;
  98. uomDesc: string;
  99. }
  100. const CompleteJobOrderRecord: React.FC<Props> = ({ filterArgs ,printerCombo}) => {
  101. const { t } = useTranslation("jo");
  102. const router = useRouter();
  103. const { data: session } = useSession() as { data: SessionWithTokens | null };
  104. const currentUserId = session?.id ? parseInt(session.id) : undefined;
  105. // 修改:已完成 Job Order Pick Orders 状态
  106. const [completedJobOrderPickOrders, setCompletedJobOrderPickOrders] = useState<CompletedJobOrderPickOrder[]>([]);
  107. const [completedJobOrderPickOrdersLoading, setCompletedJobOrderPickOrdersLoading] = useState(false);
  108. // 修改:详情视图状态
  109. const [selectedJobOrderPickOrder, setSelectedJobOrderPickOrder] = useState<CompletedJobOrderPickOrder | null>(null);
  110. const [showDetailView, setShowDetailView] = useState(false);
  111. const [detailLotData, setDetailLotData] = useState<LotDetail[]>([]);
  112. const [detailLotDataLoading, setDetailLotDataLoading] = useState(false);
  113. // 修改:搜索状态
  114. const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
  115. const [filteredJobOrderPickOrders, setFilteredJobOrderPickOrders] = useState<CompletedJobOrderPickOrder[]>([]);
  116. //const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]);
  117. const defaultDemoPrinter: PrinterCombo = {
  118. id: 2,
  119. value: 2,
  120. name: "2fi",
  121. label: "2fi",
  122. code: "2fi"
  123. };
  124. const availablePrinters = useMemo(() => {
  125. if (printerCombo.length === 0) {
  126. console.log("No printers available, using default demo printer");
  127. return [defaultDemoPrinter];
  128. }
  129. return printerCombo;
  130. }, [printerCombo]);
  131. const [selectedPrinter, setSelectedPrinter] = useState<PrinterCombo | null>(
  132. printerCombo && printerCombo.length > 0 ? printerCombo[0] : null
  133. );
  134. const [printQty, setPrintQty] = useState<number>(1);
  135. // 修改:分页状态
  136. const [paginationController, setPaginationController] = useState({
  137. pageNum: 0,
  138. pageSize: 10,
  139. });
  140. const formProps = useForm();
  141. const errors = formProps.formState.errors;
  142. // 修改:使用新的 Job Order API 获取已完成的 Job Order Pick Orders(仅完成pick的)
  143. const fetchCompletedJobOrderPickOrdersData = useCallback(async () => {
  144. if (!currentUserId) return;
  145. setCompletedJobOrderPickOrdersLoading(true);
  146. try {
  147. console.log("🔍 Fetching completed Job Order pick orders (pick completed only)...");
  148. const completedJobOrderPickOrders = await fetchCompletedJobOrderPickOrdersrecords(currentUserId);
  149. // Fix: Ensure the data is always an array
  150. const safeData = Array.isArray(completedJobOrderPickOrders) ? completedJobOrderPickOrders : [];
  151. setCompletedJobOrderPickOrders(safeData);
  152. setFilteredJobOrderPickOrders(safeData);
  153. console.log(" Fetched completed Job Order pick orders:", safeData);
  154. } catch (error) {
  155. console.error("❌ Error fetching completed Job Order pick orders:", error);
  156. setCompletedJobOrderPickOrders([]);
  157. setFilteredJobOrderPickOrders([]);
  158. } finally {
  159. setCompletedJobOrderPickOrdersLoading(false);
  160. }
  161. }, [currentUserId]);
  162. // 新增:获取 lot 详情数据(使用新的API)
  163. const fetchLotDetailsData = useCallback(async (pickOrderId: number) => {
  164. setDetailLotDataLoading(true);
  165. try {
  166. console.log("🔍 Fetching lot details for completed pick order:", pickOrderId);
  167. const lotDetails = await fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId);
  168. setDetailLotData(lotDetails);
  169. console.log(" Fetched lot details:", lotDetails);
  170. } catch (error) {
  171. console.error("❌ Error fetching lot details:", error);
  172. setDetailLotData([]);
  173. } finally {
  174. setDetailLotDataLoading(false);
  175. }
  176. }, []);
  177. // 修改:初始化时获取数据
  178. useEffect(() => {
  179. if (currentUserId) {
  180. fetchCompletedJobOrderPickOrdersData();
  181. }
  182. }, [currentUserId, fetchCompletedJobOrderPickOrdersData]);
  183. // 修改:搜索功能
  184. const handleSearch = useCallback((query: Record<string, any>) => {
  185. setSearchQuery({ ...query });
  186. console.log("Search query:", query);
  187. // Fix: Ensure completedJobOrderPickOrders is an array before filtering
  188. if (!Array.isArray(completedJobOrderPickOrders)) {
  189. setFilteredJobOrderPickOrders([]);
  190. return;
  191. }
  192. const filtered = completedJobOrderPickOrders.filter((pickOrder) => {
  193. const pickOrderCodeMatch = !query.pickOrderCode ||
  194. pickOrder.pickOrderCode?.toLowerCase().includes((query.pickOrderCode || "").toLowerCase());
  195. const jobOrderCodeMatch = !query.jobOrderCode ||
  196. pickOrder.jobOrderCode?.toLowerCase().includes((query.jobOrderCode || "").toLowerCase());
  197. const jobOrderNameMatch = !query.jobOrderName ||
  198. pickOrder.jobOrderName?.toLowerCase().includes((query.jobOrderName || "").toLowerCase());
  199. return pickOrderCodeMatch && jobOrderCodeMatch && jobOrderNameMatch;
  200. });
  201. setFilteredJobOrderPickOrders(filtered);
  202. console.log("Filtered Job Order pick orders count:", filtered.length);
  203. }, [completedJobOrderPickOrders]);
  204. // 修改:重置搜索
  205. const handleSearchReset = useCallback(() => {
  206. setSearchQuery({});
  207. // Fix: Ensure completedJobOrderPickOrders is an array before setting
  208. setFilteredJobOrderPickOrders(Array.isArray(completedJobOrderPickOrders) ? completedJobOrderPickOrders : []);
  209. }, [completedJobOrderPickOrders]);
  210. // 修改:分页功能
  211. const handlePageChange = useCallback((event: unknown, newPage: number) => {
  212. setPaginationController(prev => ({
  213. ...prev,
  214. pageNum: newPage,
  215. }));
  216. }, []);
  217. const handlePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  218. const newPageSize = parseInt(event.target.value, 10);
  219. setPaginationController({
  220. pageNum: 0,
  221. pageSize: newPageSize,
  222. });
  223. }, []);
  224. // 修改:分页数据
  225. const paginatedData = useMemo(() => {
  226. // Fix: Ensure filteredJobOrderPickOrders is an array before calling slice
  227. if (!Array.isArray(filteredJobOrderPickOrders)) {
  228. return [];
  229. }
  230. const startIndex = paginationController.pageNum * paginationController.pageSize;
  231. const endIndex = startIndex + paginationController.pageSize;
  232. return filteredJobOrderPickOrders.slice(startIndex, endIndex);
  233. }, [filteredJobOrderPickOrders, paginationController]);
  234. // 修改:搜索条件
  235. const searchCriteria: Criterion<any>[] = [
  236. {
  237. label: t("Pick Order Code"),
  238. paramName: "pickOrderCode",
  239. type: "text",
  240. },
  241. {
  242. label: t("Job Order Code"),
  243. paramName: "jobOrderCode",
  244. type: "text",
  245. },
  246. {
  247. label: t("Job Order Item Name"),
  248. paramName: "jobOrderName",
  249. type: "text",
  250. }
  251. ];
  252. // 修改:详情点击处理
  253. const handleDetailClick = useCallback(async (jobOrderPickOrder: CompletedJobOrderPickOrder) => {
  254. setSelectedJobOrderPickOrder(jobOrderPickOrder);
  255. setShowDetailView(true);
  256. // 获取 lot 详情数据(使用新的API)
  257. await fetchLotDetailsData(jobOrderPickOrder.pickOrderId);
  258. // 触发打印按钮状态更新 - 基于详情数据
  259. const allCompleted = jobOrderPickOrder.secondScanCompleted;
  260. // 发送事件,包含标签页信息
  261. window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
  262. detail: {
  263. allLotsCompleted: allCompleted,
  264. tabIndex: 3 // 明确指定这是来自标签页 3 的事件
  265. }
  266. }));
  267. }, [fetchLotDetailsData]);
  268. // 修改:返回列表视图
  269. const handleBackToList = useCallback(() => {
  270. setShowDetailView(false);
  271. setSelectedJobOrderPickOrder(null);
  272. setDetailLotData([]);
  273. // 返回列表时禁用打印按钮
  274. window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
  275. detail: {
  276. allLotsCompleted: false,
  277. tabIndex: 3
  278. }
  279. }));
  280. }, []);
  281. const handlePickRecord = useCallback(async (jobOrderPickOrder: CompletedJobOrderPickOrder) => {
  282. try {
  283. if (!jobOrderPickOrder) {
  284. console.error("No selected job order pick order available");
  285. return;
  286. }
  287. // 检查是否已选择打印机
  288. if (!selectedPrinter) {
  289. Swal.fire({
  290. position: "bottom-end",
  291. icon: "warning",
  292. text: t("Please select a printer first"),
  293. showConfirmButton: false,
  294. timer: 1500
  295. });
  296. return;
  297. }
  298. // 检查打印数量是否有效
  299. if (!printQty || printQty < 1) {
  300. Swal.fire({
  301. position: "bottom-end",
  302. icon: "warning",
  303. text: t("Please enter a valid print quantity (at least 1)"),
  304. showConfirmButton: false,
  305. timer: 1500
  306. });
  307. return;
  308. }
  309. const pickOrderId = jobOrderPickOrder.pickOrderId;
  310. console.log("Pick Order ID:", pickOrderId);
  311. // 使用已选择的打印机和数量
  312. const printerId = selectedPrinter.id;
  313. const printRequest = {
  314. pickOrderId: pickOrderId,
  315. printerId: printerId,
  316. printQty: printQty
  317. };
  318. console.log("Printing Pick Record with request: ", printRequest);
  319. const response = await PrintPickRecord(printRequest);
  320. console.log("Print Pick Record response: ", response);
  321. if (response.success) {
  322. Swal.fire({
  323. position: "bottom-end",
  324. icon: "success",
  325. text: t("Printed Successfully."),
  326. showConfirmButton: false,
  327. timer: 1500
  328. });
  329. } else {
  330. console.error("Print failed: ", response.message);
  331. Swal.fire({
  332. position: "bottom-end",
  333. icon: "error",
  334. text: response.message || t("Print failed"),
  335. showConfirmButton: false,
  336. timer: 1500
  337. });
  338. }
  339. } catch (error) {
  340. console.error("error: ", error);
  341. Swal.fire({
  342. position: "bottom-end",
  343. icon: "error",
  344. text: t("An error occurred while printing"),
  345. showConfirmButton: false,
  346. timer: 1500
  347. });
  348. }
  349. }, [t, selectedPrinter, printQty]);
  350. // 修改:如果显示详情视图,渲染 Job Order 详情和 Lot 信息
  351. if (showDetailView && selectedJobOrderPickOrder) {
  352. return (
  353. <FormProvider {...formProps}>
  354. <Box>
  355. {/* 返回按钮和标题 */}
  356. <Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}>
  357. <Button variant="outlined" onClick={handleBackToList}>
  358. {t("Back to List")}
  359. </Button>
  360. <Typography variant="h6">
  361. {t("Job Order Pick Order Details")}: {selectedJobOrderPickOrder.pickOrderCode}
  362. </Typography>
  363. </Box>
  364. {/* Job Order 信息卡片 */}
  365. <Card sx={{ mb: 2 }}>
  366. <CardContent>
  367. <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap">
  368. <Typography variant="subtitle1">
  369. <strong>{t("Pick Order Code")}:</strong> {selectedJobOrderPickOrder.pickOrderCode}
  370. </Typography>
  371. <Typography variant="subtitle1">
  372. <strong>{t("Job Order Code")}:</strong> {selectedJobOrderPickOrder.jobOrderCode}
  373. </Typography>
  374. <Typography variant="subtitle1">
  375. <strong>{t("Job Order Item Name")}:</strong> {selectedJobOrderPickOrder.jobOrderName}
  376. </Typography>
  377. <Typography variant="subtitle1">
  378. <strong>{t("Target Date")}:</strong> {selectedJobOrderPickOrder.pickOrderTargetDate}
  379. </Typography>
  380. </Stack>
  381. <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap" sx={{ mt: 2 }}>
  382. <Typography variant="subtitle1">
  383. <strong>{t("Required Qty")}:</strong> {selectedJobOrderPickOrder.reqQty} {selectedJobOrderPickOrder.uom}
  384. </Typography>
  385. </Stack>
  386. {/*
  387. <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap" sx={{ mt: 2 }}>
  388. <Button
  389. variant="contained"
  390. color="primary"
  391. onClick={() => handlePickRecord(selectedJobOrderPickOrder)}
  392. sx={{ mt: 1 }}
  393. >
  394. {t("Print Pick Record")}
  395. </Button>
  396. </Stack>
  397. */}
  398. </CardContent>
  399. </Card>
  400. {/* 修改:Lot 详情表格 - 添加复选框列 */}
  401. <Card>
  402. <CardContent>
  403. <Typography variant="h6" gutterBottom>
  404. {t("Lot Details")}
  405. </Typography>
  406. {detailLotDataLoading ? (
  407. <Box sx={{ display: 'flex', justifyContent: 'center', p: 2 }}>
  408. <CircularProgress />
  409. </Box>
  410. ) : (
  411. <TableContainer component={Paper}>
  412. <Table>
  413. <TableHead>
  414. <TableRow>
  415. <TableCell>{t("Index")}</TableCell>
  416. <TableCell>{t("Route")}</TableCell>
  417. <TableCell>{t("Item Code")}</TableCell>
  418. <TableCell>{t("Item Name")}</TableCell>
  419. <TableCell>{t("Lot No")}</TableCell>
  420. <TableCell>{t("Location")}</TableCell>
  421. <TableCell align="right">{t("Required Qty")}</TableCell>
  422. <TableCell align="right">{t("Actual Pick Qty")}</TableCell>
  423. <TableCell align="center">{t("Processing Status")}</TableCell>
  424. <TableCell align="center">{t("Second Scan Status")}</TableCell>
  425. </TableRow>
  426. </TableHead>
  427. <TableBody>
  428. {detailLotData.length === 0 ? (
  429. <TableRow>
  430. <TableCell colSpan={10} align="center">
  431. <Typography variant="body2" color="text.secondary">
  432. {t("No lot details available")}
  433. </Typography>
  434. </TableCell>
  435. </TableRow>
  436. ) : (
  437. detailLotData.map((lot, index) => (
  438. <TableRow key={lot.lotId}>
  439. <TableCell>
  440. <Typography variant="body2" fontWeight="bold">
  441. {index + 1}
  442. </Typography>
  443. </TableCell>
  444. <TableCell>
  445. <Typography variant="body2">
  446. {lot.routerRoute || '-'}
  447. </Typography>
  448. </TableCell>
  449. <TableCell>{lot.itemCode}</TableCell>
  450. <TableCell>{lot.itemName}</TableCell>
  451. <TableCell>{lot.lotNo}</TableCell>
  452. <TableCell>{lot.location}</TableCell>
  453. <TableCell align="right">
  454. {lot.requiredQty?.toLocaleString() || 0} ({lot.uomShortDesc})
  455. </TableCell>
  456. <TableCell align="right">
  457. {lot.actualPickQty?.toLocaleString() || 0} ({lot.uomShortDesc})
  458. </TableCell>
  459. {/* 修改:Processing Status 使用复选框 */}
  460. <TableCell align="center">
  461. <Box sx={{
  462. display: 'flex',
  463. justifyContent: 'center',
  464. alignItems: 'center',
  465. width: '100%',
  466. height: '100%'
  467. }}>
  468. <Checkbox
  469. checked={lot.processingStatus === 'completed'}
  470. disabled={true}
  471. readOnly={true}
  472. size="large"
  473. sx={{
  474. color: lot.processingStatus === 'completed' ? 'success.main' : 'grey.400',
  475. '&.Mui-checked': {
  476. color: 'success.main',
  477. },
  478. transform: 'scale(1.3)',
  479. '& .MuiSvgIcon-root': {
  480. fontSize: '1.5rem',
  481. }
  482. }}
  483. />
  484. </Box>
  485. </TableCell>
  486. {/* 修改:Second Scan Status 使用复选框 */}
  487. <TableCell align="center">
  488. <Box sx={{
  489. display: 'flex',
  490. justifyContent: 'center',
  491. alignItems: 'center',
  492. width: '100%',
  493. height: '100%'
  494. }}>
  495. <Checkbox
  496. checked={lot.secondQrScanStatus === 'completed'}
  497. disabled={true}
  498. readOnly={true}
  499. size="large"
  500. sx={{
  501. color: lot.secondQrScanStatus === 'completed' ? 'success.main' : 'grey.400',
  502. '&.Mui-checked': {
  503. color: 'success.main',
  504. },
  505. transform: 'scale(1.3)',
  506. '& .MuiSvgIcon-root': {
  507. fontSize: '1.5rem',
  508. }
  509. }}
  510. />
  511. </Box>
  512. </TableCell>
  513. </TableRow>
  514. ))
  515. )}
  516. </TableBody>
  517. </Table>
  518. </TableContainer>
  519. )}
  520. </CardContent>
  521. </Card>
  522. </Box>
  523. </FormProvider>
  524. );
  525. }
  526. // 修改:默认列表视图
  527. return (
  528. <FormProvider {...formProps}>
  529. <Box>
  530. {/* 搜索框 */}
  531. <Box sx={{ mb: 2 }}>
  532. <SearchBox
  533. criteria={searchCriteria}
  534. onSearch={handleSearch}
  535. onReset={handleSearchReset}
  536. />
  537. </Box>
  538. {/* 加载状态 */}
  539. {completedJobOrderPickOrdersLoading ? (
  540. <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
  541. <CircularProgress />
  542. </Box>
  543. ) : (
  544. <Box>
  545. {/* 结果统计 */}
  546. <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
  547. {t("Total")}: {filteredJobOrderPickOrders.length} {t("completed Job Order pick orders with matching")}
  548. </Typography>
  549. <Box sx={{ mb: 2, p: 2, border: '1px solid #e0e0e0', borderRadius: 1, bgcolor: 'background.paper' }}>
  550. <Stack direction="row" spacing={2} alignItems="center" flexWrap="wrap">
  551. <Typography variant="subtitle1" sx={{ minWidth: 'fit-content' }}>
  552. {t("Select Printer")}:
  553. </Typography>
  554. <Autocomplete
  555. options={availablePrinters}
  556. getOptionLabel={(option) => option.name || option.label || option.code || `Printer ${option.id}`}
  557. value={selectedPrinter}
  558. onChange={(_, newValue) => setSelectedPrinter(newValue)}
  559. sx={{ minWidth: 250 }}
  560. size="small"
  561. renderInput={(params) => <TextField {...params} label={t("Printer")} />}
  562. />
  563. <Typography variant="subtitle1" sx={{ minWidth: 'fit-content' }}>
  564. {t("Print Quantity")}:
  565. </Typography>
  566. <TextField
  567. type="number"
  568. label={t("Print Quantity")}
  569. value={printQty}
  570. onChange={(e) => {
  571. const value = parseInt(e.target.value) || 1;
  572. setPrintQty(Math.max(1, value));
  573. }}
  574. inputProps={{ min: 1, step: 1 }}
  575. sx={{ width: 120 }}
  576. size="small"
  577. />
  578. </Stack>
  579. </Box>
  580. {/* 列表 */}
  581. {filteredJobOrderPickOrders.length === 0 ? (
  582. <Box sx={{ p: 3, textAlign: 'center' }}>
  583. <Typography variant="body2" color="text.secondary">
  584. {t("No completed Job Order pick orders with matching found")}
  585. </Typography>
  586. </Box>
  587. ) : (
  588. <Stack spacing={2}>
  589. {paginatedData.map((jobOrderPickOrder) => (
  590. <Card key={jobOrderPickOrder.id}>
  591. <CardContent>
  592. <Stack direction="row" justifyContent="space-between" alignItems="center">
  593. <Box>
  594. <Typography variant="h6">
  595. {jobOrderPickOrder.jobOrderCode}
  596. </Typography>
  597. <Typography variant="body2" color="text.secondary">
  598. {jobOrderPickOrder.jobOrderName} - {jobOrderPickOrder.pickOrderCode}
  599. </Typography>
  600. <Typography variant="body2" color="text.secondary">
  601. {t("Completed")}: {new Date(jobOrderPickOrder.completedDate).toLocaleString()}
  602. </Typography>
  603. <Typography variant="body2" color="text.secondary">
  604. {t("Target Date")}: {jobOrderPickOrder.pickOrderTargetDate}
  605. </Typography>
  606. </Box>
  607. <Box>
  608. <Chip
  609. label={t(jobOrderPickOrder.pickOrderStatus) }
  610. color={jobOrderPickOrder.pickOrderStatus === 'completed' ? 'success' : 'default'}
  611. size="small"
  612. sx={{ mb: 1 }}
  613. />
  614. <Typography variant="body2" color="text.secondary">
  615. {jobOrderPickOrder.completedItems}/{jobOrderPickOrder.totalItems} {t("items completed")}
  616. </Typography>
  617. <Chip
  618. label={jobOrderPickOrder.secondScanCompleted ? t("Second Scan Completed") : t("Second Scan Pending")}
  619. color={jobOrderPickOrder.secondScanCompleted ? 'success' : 'warning'}
  620. size="small"
  621. sx={{ mt: 1 }}
  622. />
  623. </Box>
  624. </Stack>
  625. </CardContent>
  626. <CardActions>
  627. <Button
  628. variant="outlined"
  629. onClick={() => handleDetailClick(jobOrderPickOrder)}
  630. >
  631. {t("View Details")}
  632. </Button>
  633. <Button
  634. variant="contained"
  635. color="primary"
  636. onClick={() => handlePickRecord(jobOrderPickOrder)}
  637. sx={{ mt: 1 }}
  638. >
  639. {t("Print Pick Record")}
  640. </Button>
  641. </CardActions>
  642. </Card>
  643. ))}
  644. </Stack>
  645. )}
  646. {/* 分页 */}
  647. {filteredJobOrderPickOrders.length > 0 && (
  648. <TablePagination
  649. component="div"
  650. count={filteredJobOrderPickOrders.length}
  651. page={paginationController.pageNum}
  652. rowsPerPage={paginationController.pageSize}
  653. onPageChange={handlePageChange}
  654. onRowsPerPageChange={handlePageSizeChange}
  655. rowsPerPageOptions={[5, 10, 25, 50]}
  656. />
  657. )}
  658. </Box>
  659. )}
  660. </Box>
  661. </FormProvider>
  662. );
  663. };
  664. export default CompleteJobOrderRecord;