FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

completeJobOrderRecord.tsx 25 KiB

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