FPSMS-frontend
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 

740 líneas
25 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. } from "@mui/material";
  27. import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
  28. import { useCallback, useEffect, useState, useRef, useMemo } from "react";
  29. import { useTranslation } from "react-i18next";
  30. import { useRouter } from "next/navigation";
  31. import {
  32. fetchALLPickOrderLineLotDetails,
  33. updateStockOutLineStatus,
  34. createStockOutLine,
  35. recordPickExecutionIssue,
  36. fetchFGPickOrders,
  37. FGPickOrderResponse,
  38. autoAssignAndReleasePickOrder,
  39. AutoAssignReleaseResponse,
  40. checkPickOrderCompletion,
  41. PickOrderCompletionResponse,
  42. checkAndCompletePickOrderByConsoCode,
  43. fetchCompletedDoPickOrders,
  44. CompletedDoPickOrderResponse,
  45. CompletedDoPickOrderSearchParams,
  46. fetchLotDetailsByDoPickOrderRecordId
  47. } from "@/app/api/pickOrder/actions";
  48. import { fetchNameList, NameList } from "@/app/api/user/actions";
  49. import {
  50. FormProvider,
  51. useForm,
  52. } from "react-hook-form";
  53. import SearchBox, { Criterion } from "../SearchBox";
  54. import { CreateStockOutLine } from "@/app/api/pickOrder/actions";
  55. import { updateInventoryLotLineQuantities } from "@/app/api/inventory/actions";
  56. import QrCodeIcon from '@mui/icons-material/QrCode';
  57. import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
  58. import { useSession } from "next-auth/react";
  59. import { SessionWithTokens } from "@/config/authConfig";
  60. import { fetchStockInLineInfo } from "@/app/api/po/actions";
  61. import GoodPickExecutionForm from "./GoodPickExecutionForm";
  62. import FGPickOrderCard from "./FGPickOrderCard";
  63. import dayjs from "dayjs";
  64. import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  65. import { printDN, printDNLabels } from "@/app/api/do/actions";
  66. import Swal from "sweetalert2";
  67. interface Props {
  68. filterArgs: Record<string, any>;
  69. }
  70. // ✅ 新增:Pick Order 数据接口
  71. interface PickOrderData {
  72. pickOrderId: number;
  73. pickOrderCode: string;
  74. pickOrderConsoCode: string;
  75. pickOrderStatus: string;
  76. completedDate: string;
  77. lots: any[];
  78. }
  79. const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => {
  80. const { t } = useTranslation("pickOrder");
  81. const router = useRouter();
  82. const { data: session } = useSession() as { data: SessionWithTokens | null };
  83. const currentUserId = session?.id ? parseInt(session.id) : undefined;
  84. // ✅ 新增:已完成 DO Pick Orders 状态
  85. const [completedDoPickOrders, setCompletedDoPickOrders] = useState<CompletedDoPickOrderResponse[]>([]);
  86. const [completedDoPickOrdersLoading, setCompletedDoPickOrdersLoading] = useState(false);
  87. // ✅ 新增:详情视图状态
  88. const [selectedDoPickOrder, setSelectedDoPickOrder] = useState<CompletedDoPickOrderResponse | null>(null);
  89. const [showDetailView, setShowDetailView] = useState(false);
  90. const [detailLotData, setDetailLotData] = useState<any[]>([]);
  91. // ✅ 新增:搜索状态
  92. const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
  93. const [filteredDoPickOrders, setFilteredDoPickOrders] = useState<CompletedDoPickOrderResponse[]>([]);
  94. // ✅ 新增:分页状态
  95. const [paginationController, setPaginationController] = useState({
  96. pageNum: 0,
  97. pageSize: 10,
  98. });
  99. const formProps = useForm();
  100. const errors = formProps.formState.errors;
  101. const handleDN = useCallback(async (doPickOrderId: number) => {
  102. const askNumofCarton = await Swal.fire({
  103. title: t("Enter the number of cartons: "),
  104. icon: "info",
  105. input: "number",
  106. inputPlaceholder: t("Number of cartons"),
  107. inputAttributes:{
  108. min: "1",
  109. step: "1"
  110. },
  111. inputValidator: (value) => {
  112. if(!value){
  113. return t("You need to enter a number")
  114. }
  115. if(parseInt(value) < 1){
  116. return t("Number must be at least 1");
  117. }
  118. return null
  119. },
  120. showCancelButton: true,
  121. confirmButtonText: t("Confirm"),
  122. cancelButtonText: t("Cancel"),
  123. confirmButtonColor: "#8dba00",
  124. cancelButtonColor: "#F04438",
  125. showLoaderOnConfirm: true,
  126. allowOutsideClick: () => !Swal.isLoading()
  127. });
  128. if (askNumofCarton.isConfirmed) {
  129. const numOfCartons = askNumofCarton.value;
  130. try{
  131. const printRequest = {
  132. printerId: 1,
  133. printQty: 1,
  134. isDraft: false,
  135. numOfCarton: numOfCartons,
  136. doPickOrderId: doPickOrderId
  137. };
  138. console.log("Printing Delivery Note with request: ", printRequest);
  139. const response = await printDN(printRequest);
  140. console.log("Print Delivery Note response: ", response);
  141. if(response.success){
  142. Swal.fire({
  143. position: "bottom-end",
  144. icon: "success",
  145. text: t("Printed Successfully."),
  146. showConfirmButton: false,
  147. timer: 1500
  148. });
  149. } else {
  150. console.error("Print failed: ", response.message);
  151. }
  152. } catch(error){
  153. console.error("error: ", error)
  154. }
  155. }
  156. }, [t]);
  157. const handleDNandLabel = useCallback(async (doPickOrderId: number) => {
  158. const askNumofCarton = await Swal.fire({
  159. title: t("Enter the number of cartons: "),
  160. icon: "info",
  161. input: "number",
  162. inputPlaceholder: t("Number of cartons"),
  163. inputAttributes:{
  164. min: "1",
  165. step: "1"
  166. },
  167. inputValidator: (value) => {
  168. if(!value){
  169. return t("You need to enter a number")
  170. }
  171. if(parseInt(value) < 1){
  172. return t("Number must be at least 1");
  173. }
  174. return null
  175. },
  176. showCancelButton: true,
  177. confirmButtonText: t("Confirm"),
  178. cancelButtonText: t("Cancel"),
  179. confirmButtonColor: "#8dba00",
  180. cancelButtonColor: "#F04438",
  181. showLoaderOnConfirm: true,
  182. allowOutsideClick: () => !Swal.isLoading()
  183. });
  184. if (askNumofCarton.isConfirmed) {
  185. const numOfCartons = askNumofCarton.value;
  186. try{
  187. const printDNRequest = {
  188. printerId: 1,
  189. printQty: 1,
  190. isDraft: false,
  191. numOfCarton: numOfCartons,
  192. doPickOrderId: doPickOrderId,
  193. };
  194. const printDNLabelsRequest = {
  195. printerId: 1,
  196. printQty: 1,
  197. numOfCarton: numOfCartons,
  198. doPickOrderId: doPickOrderId
  199. };
  200. console.log("Printing Labels with request: ", printDNLabelsRequest);
  201. console.log("Printing DN with request: ", printDNRequest);
  202. const LabelsResponse = await printDNLabels(printDNLabelsRequest);
  203. const DNResponse = await printDN(printDNRequest);
  204. console.log("Print Labels response: ", LabelsResponse);
  205. console.log("Print DN response: ", DNResponse);
  206. if(LabelsResponse.success && DNResponse.success){
  207. Swal.fire({
  208. position: "bottom-end",
  209. icon: "success",
  210. text: t("Printed Successfully."),
  211. showConfirmButton: false,
  212. timer: 1500
  213. });
  214. } else {
  215. if(!LabelsResponse.success){
  216. console.error("Print failed: ", LabelsResponse.message);
  217. }
  218. else{
  219. console.error("Print failed: ", DNResponse.message);
  220. }
  221. }
  222. } catch(error){
  223. console.error("error: ", error)
  224. }
  225. }
  226. }, [t]);
  227. const handleLabel = useCallback(async (doPickOrderId: number) => {
  228. const askNumofCarton = await Swal.fire({
  229. title: t("Enter the number of cartons: "),
  230. icon: "info",
  231. input: "number",
  232. inputPlaceholder: t("Number of cartons"),
  233. inputAttributes:{
  234. min: "1",
  235. step: "1"
  236. },
  237. inputValidator: (value) => {
  238. if(!value){
  239. return t("You need to enter a number")
  240. }
  241. if(parseInt(value) < 1){
  242. return t("Number must be at least 1");
  243. }
  244. return null
  245. },
  246. showCancelButton: true,
  247. confirmButtonText: t("Confirm"),
  248. cancelButtonText: t("Cancel"),
  249. confirmButtonColor: "#8dba00",
  250. cancelButtonColor: "#F04438",
  251. showLoaderOnConfirm: true,
  252. allowOutsideClick: () => !Swal.isLoading()
  253. });
  254. if (askNumofCarton.isConfirmed) {
  255. const numOfCartons = askNumofCarton.value;
  256. try{
  257. const printRequest = {
  258. printerId: 1,
  259. printQty: 1,
  260. numOfCarton: numOfCartons,
  261. doPickOrderId: doPickOrderId
  262. };
  263. console.log("Printing Labels with request: ", printRequest);
  264. const response = await printDNLabels(printRequest);
  265. console.log("Print Labels response: ", response);
  266. if(response.success){
  267. Swal.fire({
  268. position: "bottom-end",
  269. icon: "success",
  270. text: t("Printed Successfully."),
  271. showConfirmButton: false,
  272. timer: 1500
  273. });
  274. } else {
  275. console.error("Print failed: ", response.message);
  276. }
  277. } catch(error){
  278. console.error("error: ", error)
  279. }
  280. }
  281. }, [t]);
  282. // ✅ 修改:使用新的 API 获取已完成的 DO Pick Orders
  283. const fetchCompletedDoPickOrdersData = useCallback(async (searchParams?: CompletedDoPickOrderSearchParams) => {
  284. if (!currentUserId) return;
  285. setCompletedDoPickOrdersLoading(true);
  286. try {
  287. console.log("🔍 Fetching completed DO pick orders with params:", searchParams);
  288. const completedDoPickOrders = await fetchCompletedDoPickOrders(currentUserId, searchParams);
  289. setCompletedDoPickOrders(completedDoPickOrders);
  290. setFilteredDoPickOrders(completedDoPickOrders);
  291. console.log("✅ Fetched completed DO pick orders:", completedDoPickOrders);
  292. } catch (error) {
  293. console.error("❌ Error fetching completed DO pick orders:", error);
  294. setCompletedDoPickOrders([]);
  295. setFilteredDoPickOrders([]);
  296. } finally {
  297. setCompletedDoPickOrdersLoading(false);
  298. }
  299. }, [currentUserId]);
  300. // ✅ 初始化时获取数据
  301. useEffect(() => {
  302. if (currentUserId) {
  303. fetchCompletedDoPickOrdersData();
  304. }
  305. }, [currentUserId, fetchCompletedDoPickOrdersData]);
  306. // ✅ 修改:搜索功能使用新的 API
  307. const handleSearch = useCallback((query: Record<string, any>) => {
  308. setSearchQuery({ ...query });
  309. console.log("Search query:", query);
  310. const searchParams: CompletedDoPickOrderSearchParams = {
  311. pickOrderCode: query.pickOrderCode || undefined,
  312. shopName: query.shopName || undefined,
  313. deliveryNo: query.deliveryNo || undefined,
  314. //ticketNo: query.ticketNo || undefined,
  315. };
  316. // 使用新的 API 进行搜索
  317. fetchCompletedDoPickOrdersData(searchParams);
  318. }, [fetchCompletedDoPickOrdersData]);
  319. // ✅ 修复:重命名函数避免重复声明
  320. const handleSearchReset = useCallback(() => {
  321. setSearchQuery({});
  322. fetchCompletedDoPickOrdersData(); // 重新获取所有数据
  323. }, [fetchCompletedDoPickOrdersData]);
  324. // ✅ 分页功能
  325. const handlePageChange = useCallback((event: unknown, newPage: number) => {
  326. setPaginationController(prev => ({
  327. ...prev,
  328. pageNum: newPage,
  329. }));
  330. }, []);
  331. const handlePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  332. const newPageSize = parseInt(event.target.value, 10);
  333. setPaginationController({
  334. pageNum: 0,
  335. pageSize: newPageSize,
  336. });
  337. }, []);
  338. // ✅ 分页数据
  339. const paginatedData = useMemo(() => {
  340. const startIndex = paginationController.pageNum * paginationController.pageSize;
  341. const endIndex = startIndex + paginationController.pageSize;
  342. return filteredDoPickOrders.slice(startIndex, endIndex);
  343. }, [filteredDoPickOrders, paginationController]);
  344. // ✅ 搜索条件
  345. const searchCriteria: Criterion<any>[] = [
  346. {
  347. label: t("Pick Order Code"),
  348. paramName: "pickOrderCode",
  349. type: "text",
  350. },
  351. {
  352. label: t("Shop Name"),
  353. paramName: "shopName",
  354. type: "text",
  355. },
  356. {
  357. label: t("Delivery No"),
  358. paramName: "deliveryNo",
  359. type: "text",
  360. }
  361. ];
  362. const handleDetailClick = useCallback(async (doPickOrder: CompletedDoPickOrderResponse) => {
  363. setSelectedDoPickOrder(doPickOrder);
  364. setShowDetailView(true);
  365. try {
  366. // ✅ 使用 doPickOrderRecordId 而不是 pickOrderId
  367. const hierarchicalData = await fetchLotDetailsByDoPickOrderRecordId(doPickOrder.doPickOrderRecordId);
  368. console.log("✅ Loaded hierarchical lot data:", hierarchicalData);
  369. // ✅ 转换为平铺格式
  370. const flatLotData: any[] = [];
  371. if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) {
  372. hierarchicalData.pickOrders.forEach((po: any) => {
  373. po.pickOrderLines?.forEach((line: any) => {
  374. if (line.lots && line.lots.length > 0) {
  375. line.lots.forEach((lot: any) => {
  376. flatLotData.push({
  377. pickOrderCode: po.pickOrderCode,
  378. itemCode: line.item.code,
  379. itemName: line.item.name,
  380. lotNo: lot.lotNo,
  381. location: lot.location,
  382. deliveryOrderCode: po.deliveryOrderCode,
  383. requiredQty: lot.requiredQty,
  384. actualPickQty: lot.actualPickQty,
  385. processingStatus: lot.processingStatus,
  386. stockOutLineStatus: lot.stockOutLineStatus
  387. });
  388. });
  389. }
  390. });
  391. });
  392. }
  393. setDetailLotData(flatLotData);
  394. // ✅ 计算完成状态
  395. const allCompleted = flatLotData.length > 0 && flatLotData.every(lot =>
  396. lot.processingStatus === 'completed'
  397. );
  398. window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
  399. detail: {
  400. allLotsCompleted: allCompleted,
  401. tabIndex: 2
  402. }
  403. }));
  404. } catch (error) { // ✅ 添加 catch 块
  405. console.error("❌ Error loading detail lot data:", error);
  406. setDetailLotData([]);
  407. window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
  408. detail: {
  409. allLotsCompleted: false,
  410. tabIndex: 2
  411. }
  412. }));
  413. }
  414. }, []);
  415. // ✅ 返回列表视图
  416. const handleBackToList = useCallback(() => {
  417. setShowDetailView(false);
  418. setSelectedDoPickOrder(null);
  419. setDetailLotData([]);
  420. // ✅ 返回列表时禁用打印按钮
  421. window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', {
  422. detail: {
  423. allLotsCompleted: false,
  424. tabIndex: 2
  425. }
  426. }));
  427. }, []);
  428. // ✅ 如果显示详情视图,渲染类似 GoodPickExecution 的表格
  429. // ✅ 如果显示详情视图,渲染层级结构
  430. if (showDetailView && selectedDoPickOrder) {
  431. return (
  432. <FormProvider {...formProps}>
  433. <Box>
  434. {/* 返回按钮和标题 */}
  435. <Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}>
  436. <Button variant="outlined" onClick={handleBackToList}>
  437. {t("Back to List")}
  438. </Button>
  439. <Typography variant="h6">
  440. {t("Pick Order Details")}: {selectedDoPickOrder.ticketNo}
  441. </Typography>
  442. </Box>
  443. {/* FG 订单基本信息 */}
  444. <Paper sx={{ mb: 2, p: 2 }}>
  445. <Stack spacing={1}>
  446. <Typography variant="subtitle1">
  447. <strong>{t("Shop Name")}:</strong> {selectedDoPickOrder.shopName}
  448. </Typography>
  449. <Typography variant="subtitle1">
  450. <strong>{t("Store ID")}:</strong> {selectedDoPickOrder.storeId}
  451. </Typography>
  452. <Typography variant="subtitle1">
  453. <strong>{t("Ticket No.")}:</strong> {selectedDoPickOrder.ticketNo}
  454. </Typography>
  455. <Typography variant="subtitle1">
  456. <strong>{t("Truck Lance Code")}:</strong> {selectedDoPickOrder.truckLanceCode}
  457. </Typography>
  458. <Typography variant="subtitle1">
  459. <strong>{t("Completed Date")}:</strong> {dayjs(selectedDoPickOrder.completedDate).format(OUTPUT_DATE_FORMAT)}
  460. </Typography>
  461. </Stack>
  462. </Paper>
  463. {/* ✅ 添加:多个 Pick Orders 信息(如果有) */}
  464. {selectedDoPickOrder.pickOrderIds && selectedDoPickOrder.pickOrderIds.length > 1 && (
  465. <Paper sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5' }}>
  466. <Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>
  467. {t("This ticket contains")} {selectedDoPickOrder.pickOrderIds.length} {t("pick orders")}:
  468. </Typography>
  469. <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
  470. {selectedDoPickOrder.pickOrderCodes?.split(', ').map((code, idx) => (
  471. <Chip
  472. key={idx}
  473. label={code}
  474. size="small"
  475. variant="outlined"
  476. />
  477. ))}
  478. </Box>
  479. </Paper>
  480. )}
  481. {/* ✅ 数据检查 */}
  482. {detailLotData.length === 0 ? (
  483. <Box sx={{ p: 3, textAlign: 'center' }}>
  484. <Typography variant="body2" color="text.secondary">
  485. {t("No lot details found for this order")}
  486. </Typography>
  487. </Box>
  488. ) : (
  489. /* ✅ 按 Pick Order 分组显示 */
  490. <Stack spacing={2}>
  491. {/* ✅ 按 pickOrderCode 分组 */}
  492. {Object.entries(
  493. detailLotData.reduce((acc: any, lot: any) => {
  494. const key = lot.pickOrderCode || 'Unknown';
  495. if (!acc[key]) {
  496. acc[key] = {
  497. lots: [],
  498. deliveryOrderCode: lot.deliveryOrderCode || 'N/A' // ✅ 保存对应的 deliveryOrderCode
  499. };
  500. }
  501. acc[key].lots.push(lot);
  502. return acc;
  503. }, {})
  504. ).map(([pickOrderCode, data]: [string, any]) => (
  505. <Accordion key={pickOrderCode} defaultExpanded={true}>
  506. <AccordionSummary expandIcon={<ExpandMoreIcon />}>
  507. <Typography variant="subtitle1" fontWeight="bold">
  508. {t("Pick Order")}: {pickOrderCode} ({data.lots.length} {t("items")})
  509. {" | "}
  510. {t("Delivery Order")}: {data.deliveryOrderCode} {/* ✅ 使用保存的 deliveryOrderCode */}
  511. </Typography>
  512. </AccordionSummary>
  513. <AccordionDetails>
  514. <TableContainer component={Paper}>
  515. <Table size="small">
  516. <TableHead>
  517. <TableRow>
  518. <TableCell>{t("Index")}</TableCell>
  519. <TableCell>{t("Item Code")}</TableCell>
  520. <TableCell>{t("Item Name")}</TableCell>
  521. <TableCell>{t("Lot No")}</TableCell>
  522. <TableCell>{t("Location")}</TableCell>
  523. <TableCell align="right">{t("Required Qty")}</TableCell>
  524. <TableCell align="right">{t("Actual Pick Qty")}</TableCell>
  525. <TableCell align="center">{t("Status")}</TableCell>
  526. </TableRow>
  527. </TableHead>
  528. <TableBody>
  529. {data.lots.map((lot: any, index: number) => (
  530. <TableRow key={index}>
  531. <TableCell>{index + 1}</TableCell>
  532. <TableCell>{lot.itemCode || 'N/A'}</TableCell>
  533. <TableCell>{lot.itemName || 'N/A'}</TableCell>
  534. <TableCell>{lot.lotNo || 'N/A'}</TableCell>
  535. <TableCell>{lot.location || 'N/A'}</TableCell>
  536. <TableCell align="right">{lot.requiredQty || 0}</TableCell>
  537. <TableCell align="right">{lot.actualPickQty || 0}</TableCell>
  538. <TableCell align="center">
  539. <Chip
  540. label={t(lot.processingStatus || 'unknown')}
  541. color={lot.processingStatus === 'completed' ? 'success' : 'default'}
  542. size="small"
  543. />
  544. </TableCell>
  545. </TableRow>
  546. ))}
  547. </TableBody>
  548. </Table>
  549. </TableContainer>
  550. </AccordionDetails>
  551. </Accordion>
  552. ))}
  553. </Stack>
  554. )}
  555. </Box>
  556. </FormProvider>
  557. );
  558. }
  559. // ✅ 默认列表视图
  560. return (
  561. <FormProvider {...formProps}>
  562. <Box>
  563. {/* 搜索框 */}
  564. <Box sx={{ mb: 2 }}>
  565. <SearchBox
  566. criteria={searchCriteria}
  567. onSearch={handleSearch}
  568. onReset={handleSearchReset}
  569. />
  570. </Box>
  571. {/* 加载状态 */}
  572. {completedDoPickOrdersLoading ? (
  573. <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
  574. <CircularProgress />
  575. </Box>
  576. ) : (
  577. <Box>
  578. {/* 结果统计 */}
  579. <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
  580. {t("Completed DO pick orders: ")} {filteredDoPickOrders.length}
  581. </Typography>
  582. {/* 列表 */}
  583. {filteredDoPickOrders.length === 0 ? (
  584. <Box sx={{ p: 3, textAlign: 'center' }}>
  585. <Typography variant="body2" color="text.secondary">
  586. {t("No completed DO pick orders found")}
  587. </Typography>
  588. </Box>
  589. ) : (
  590. <Stack spacing={2}>
  591. {paginatedData.map((doPickOrder) => (
  592. <Card key={doPickOrder.id}>
  593. <CardContent>
  594. <Stack direction="row" justifyContent="space-between" alignItems="center">
  595. <Box>
  596. <Typography variant="h6">
  597. {doPickOrder.pickOrderCode}
  598. </Typography>
  599. <Typography variant="body2" color="text.secondary">
  600. {doPickOrder.shopName} - {doPickOrder.deliveryNo}
  601. </Typography>
  602. <Typography variant="body2" color="text.secondary">
  603. {t("Completed")}: {dayjs(doPickOrder.completedDate).format(OUTPUT_DATE_FORMAT)}
  604. </Typography>
  605. </Box>
  606. <Box>
  607. <Chip
  608. label={t(doPickOrder.pickOrderStatus)}
  609. color={doPickOrder.pickOrderStatus === 'completed' ? 'success' : 'default'}
  610. size="small"
  611. sx={{ mb: 1 }}
  612. />
  613. <Typography variant="body2" color="text.secondary">
  614. {doPickOrder.fgPickOrders.length} {t("FG orders")}
  615. </Typography>
  616. </Box>
  617. </Stack>
  618. </CardContent>
  619. <CardActions>
  620. <Button
  621. variant="outlined"
  622. onClick={() => handleDetailClick(doPickOrder)}
  623. >
  624. {t("View Details")}
  625. </Button>
  626. <>
  627. <Button
  628. variant="contained"
  629. onClick={() => handleDN(
  630. doPickOrder.id
  631. )}
  632. >
  633. {t("Print Pick Order")}
  634. </Button>
  635. <Button
  636. variant="contained"
  637. onClick={() => handleDNandLabel(
  638. doPickOrder.id
  639. )}
  640. >
  641. {t("Print DN & Label")}
  642. </Button>
  643. <Button
  644. variant="contained"
  645. onClick={() => handleLabel(
  646. doPickOrder.id
  647. )}
  648. >
  649. {t("Print Label")}
  650. </Button>
  651. </>
  652. )
  653. </CardActions>
  654. </Card>
  655. ))}
  656. </Stack>
  657. )}
  658. {/* 分页 */}
  659. {filteredDoPickOrders.length > 0 && (
  660. <TablePagination
  661. component="div"
  662. count={filteredDoPickOrders.length}
  663. page={paginationController.pageNum}
  664. rowsPerPage={paginationController.pageSize}
  665. onPageChange={handlePageChange}
  666. onRowsPerPageChange={handlePageSizeChange}
  667. rowsPerPageOptions={[5, 10, 25, 50]}
  668. />
  669. )}
  670. </Box>
  671. )}
  672. </Box>
  673. </FormProvider>
  674. );
  675. };
  676. export default GoodPickExecutionRecord;