FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

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