| @@ -681,6 +681,25 @@ export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): P | |||
| }; | |||
| } | |||
| }); | |||
| export const fetchLotDetailsByPickOrderId = async (pickOrderId: number): Promise<any[]> => { | |||
| try { | |||
| console.log("🔍 Fetching lot details for pickOrderId:", pickOrderId); | |||
| const data = await serverFetchJson<any[]>( | |||
| `${BASE_API_URL}/pickOrder/lot-details-by-pick-order/${pickOrderId}`, | |||
| { | |||
| method: 'GET', | |||
| next: { tags: ["pickorder"] }, | |||
| } | |||
| ); | |||
| console.log("✅ Fetched lot details for pickOrderId:", data); | |||
| return data; | |||
| } catch (error) { | |||
| console.error("❌ Error fetching lot details for pickOrderId:", error); | |||
| return []; | |||
| } | |||
| }; | |||
| // Update the existing function to use the non-auto-assign endpoint | |||
| export const fetchALLPickOrderLineLotDetails = cache(async (userId: number): Promise<any[]> => { | |||
| try { | |||
| @@ -5,7 +5,7 @@ const ApexCharts = dynamic(() => import("react-apexcharts"), { ssr: false }); | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplicationCompletionChart: React.FC = () => { | |||
| const { t } = useTranslation(); | |||
| const [tab, setTab] = useState(t("Raw material")); | |||
| const [tab, setTab] = useState(t("Store Management")); | |||
| const percent = 0; | |||
| const options = { | |||
| chart: { type: "donut" as const }, | |||
| @@ -68,19 +68,19 @@ const ApplicationCompletionChart: React.FC = () => { | |||
| <button | |||
| style={{ | |||
| border: | |||
| tab === t("Raw material") | |||
| tab === t("Raw MA") | |||
| ? "1px solid #1976d2" | |||
| : "1px solid #e0e0e0", | |||
| background: tab === t("Raw material") ? "#fff" : "#f5f5f5", | |||
| color: tab === t("Raw material") ? "#1976d2" : "#333", | |||
| background: tab === t("Store Management") ? "#fff" : "#f5f5f5", | |||
| color: tab === t("Store Management") ? "#1976d2" : "#333", | |||
| borderRadius: 4, | |||
| padding: "2px 12px", | |||
| marginRight: 4, | |||
| cursor: "pointer", | |||
| }} | |||
| onClick={() => setTab(t("Raw material"))} | |||
| onClick={() => setTab(t("Store Management"))} | |||
| > | |||
| {t("Raw material")} | |||
| {t("Store Management")} | |||
| </button> | |||
| <button | |||
| style={{ | |||
| @@ -5,7 +5,7 @@ const ApexCharts = dynamic(() => import("react-apexcharts"), { ssr: false }); | |||
| import { useTranslation } from "react-i18next"; | |||
| const OrderCompletionChart: React.FC = () => { | |||
| const { t } = useTranslation(); | |||
| const [tab, setTab] = useState(t("Raw material")); | |||
| const [tab, setTab] = useState(t("Store Management")); | |||
| const percent = 0; | |||
| const options = { | |||
| chart: { type: "donut" as const }, | |||
| @@ -63,19 +63,19 @@ const OrderCompletionChart: React.FC = () => { | |||
| <button | |||
| style={{ | |||
| border: | |||
| tab === t("Raw material") | |||
| tab === t("Store Management") | |||
| ? "1px solid #1976d2" | |||
| : "1px solid #e0e0e0", | |||
| background: tab === t("Raw material") ? "#fff" : "#f5f5f5", | |||
| color: tab === t("Raw material") ? "#1976d2" : "#333", | |||
| background: tab === t("Store Management") ? "#fff" : "#f5f5f5", | |||
| color: tab === t("Store Management") ? "#1976d2" : "#333", | |||
| borderRadius: 4, | |||
| padding: "2px 12px", | |||
| marginRight: 4, | |||
| cursor: "pointer", | |||
| }} | |||
| onClick={() => setTab(t("Raw material"))} | |||
| onClick={() => setTab(t("Store Management"))} | |||
| > | |||
| {t("Raw material")} | |||
| {t("Store Management")} | |||
| </button> | |||
| <button | |||
| style={{ | |||
| @@ -65,6 +65,36 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { | |||
| window.addEventListener('pickOrderAssigned', onAssigned); | |||
| return () => window.removeEventListener('pickOrderAssigned', onAssigned); | |||
| }, []); | |||
| // ... existing code ... | |||
| useEffect(() => { | |||
| const handleCompletionStatusChange = (event: CustomEvent) => { | |||
| const { allLotsCompleted, tabIndex: eventTabIndex } = event.detail; | |||
| // ✅ 修复:根据标签页和事件来源决定是否更新打印按钮状态 | |||
| if (eventTabIndex === undefined || eventTabIndex === tabIndex) { | |||
| setPrintButtonsEnabled(allLotsCompleted); | |||
| console.log(`Print buttons enabled for tab ${tabIndex}:`, allLotsCompleted); | |||
| } | |||
| }; | |||
| window.addEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener); | |||
| return () => { | |||
| window.removeEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener); | |||
| }; | |||
| }, [tabIndex]); // ✅ 添加 tabIndex 依赖 | |||
| // ✅ 新增:处理标签页切换时的打印按钮状态重置 | |||
| useEffect(() => { | |||
| // 当切换到标签页 2 (GoodPickExecutionRecord) 时,重置打印按钮状态 | |||
| if (tabIndex === 2) { | |||
| setPrintButtonsEnabled(false); | |||
| console.log("Reset print buttons for Pick Execution Record tab"); | |||
| } | |||
| }, [tabIndex]); | |||
| // ... existing code ... | |||
| const handleAssignByStore = async (storeId: "2/F" | "4/F") => { | |||
| if (!currentUserId) { | |||
| console.error("Missing user id in session"); | |||
| @@ -420,10 +420,32 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||
| console.log("✅ All combined lot details:", allLotDetails); | |||
| setCombinedLotData(allLotDetails); | |||
| setOriginalCombinedData(allLotDetails); | |||
| // ✅ 计算完成状态并发送事件 | |||
| const allCompleted = allLotDetails.length > 0 && allLotDetails.every(lot => | |||
| lot.processingStatus === 'completed' | |||
| ); | |||
| // ✅ 发送完成状态事件,包含标签页信息 | |||
| window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { | |||
| allLotsCompleted: allCompleted, | |||
| tabIndex: 0 // ✅ 明确指定这是来自标签页 0 的事件 | |||
| } | |||
| })); | |||
| } catch (error) { | |||
| console.error("❌ Error fetching combined lot data:", error); | |||
| setCombinedLotData([]); | |||
| setOriginalCombinedData([]); | |||
| // ✅ 如果加载失败,禁用打印按钮 | |||
| window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { | |||
| allLotsCompleted: false, | |||
| tabIndex: 0 | |||
| } | |||
| })); | |||
| } finally { | |||
| setCombinedDataLoading(false); | |||
| } | |||
| @@ -43,7 +43,8 @@ import { | |||
| checkAndCompletePickOrderByConsoCode, | |||
| fetchCompletedDoPickOrders, // ✅ 新增:使用新的 API | |||
| CompletedDoPickOrderResponse, | |||
| CompletedDoPickOrderSearchParams // ✅ 修复:导入类型 | |||
| CompletedDoPickOrderSearchParams, | |||
| fetchLotDetailsByPickOrderId // ✅ 修复:导入类型 | |||
| } from "@/app/api/pickOrder/actions"; | |||
| import { fetchNameList, NameList } from "@/app/api/user/actions"; | |||
| import { | |||
| @@ -165,7 +166,7 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => { | |||
| pickOrderCode: query.pickOrderCode || undefined, | |||
| shopName: query.shopName || undefined, | |||
| deliveryNo: query.deliveryNo || undefined, | |||
| ticketNo: query.ticketNo || undefined, | |||
| //ticketNo: query.ticketNo || undefined, | |||
| }; | |||
| // 使用新的 API 进行搜索 | |||
| @@ -217,40 +218,63 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => { | |||
| label: t("Delivery No"), | |||
| paramName: "deliveryNo", | |||
| type: "text", | |||
| }, | |||
| { | |||
| label: t("Ticket No"), | |||
| paramName: "ticketNo", | |||
| type: "text", | |||
| }, | |||
| } | |||
| ]; | |||
| // ✅ 处理详情点击 - 显示类似 GoodPickExecution 的表格 | |||
| const handleDetailClick = useCallback(async (doPickOrder: CompletedDoPickOrder) => { | |||
| setSelectedDoPickOrder(doPickOrder); | |||
| setShowDetailView(true); | |||
| // 获取该 pick order 的详细 lot 数据 | |||
| // ✅ 修复:使用新的 API 根据 pickOrderId 获取 lot 详情 | |||
| try { | |||
| const allLotDetails = await fetchALLPickOrderLineLotDetails(currentUserId!); | |||
| const filteredLots = allLotDetails.filter(lot => | |||
| lot.pickOrderId === doPickOrder.pickOrderId | |||
| const lotDetails = await fetchLotDetailsByPickOrderId(doPickOrder.pickOrderId); | |||
| setDetailLotData(lotDetails); | |||
| console.log("✅ Loaded detail lot data for pick order:", doPickOrder.pickOrderCode, lotDetails); | |||
| // ✅ 触发打印按钮状态更新 - 基于详情数据 | |||
| const allCompleted = lotDetails.length > 0 && lotDetails.every(lot => | |||
| lot.processingStatus === 'completed' | |||
| ); | |||
| setDetailLotData(filteredLots); | |||
| console.log("✅ Loaded detail lot data for pick order:", doPickOrder.pickOrderCode, filteredLots); | |||
| // ✅ 发送事件,包含标签页信息 | |||
| window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { | |||
| allLotsCompleted: allCompleted, | |||
| tabIndex: 2 // ✅ 明确指定这是来自标签页 2 的事件 | |||
| } | |||
| })); | |||
| } catch (error) { | |||
| console.error("❌ Error loading detail lot data:", error); | |||
| setDetailLotData([]); | |||
| // ✅ 如果加载失败,禁用打印按钮 | |||
| window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { | |||
| allLotsCompleted: false, | |||
| tabIndex: 2 | |||
| } | |||
| })); | |||
| } | |||
| }, [currentUserId]); | |||
| }, []); | |||
| // ✅ 返回列表视图 | |||
| const handleBackToList = useCallback(() => { | |||
| setShowDetailView(false); | |||
| setSelectedDoPickOrder(null); | |||
| setDetailLotData([]); | |||
| // ✅ 返回列表时禁用打印按钮 | |||
| window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { | |||
| allLotsCompleted: false, | |||
| tabIndex: 2 | |||
| } | |||
| })); | |||
| }, []); | |||
| // ✅ 如果显示详情视图,渲染类似 GoodPickExecution 的表格 | |||
| if (showDetailView && selectedDoPickOrder) { | |||
| return ( | |||
| @@ -555,7 +555,10 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||
| useEffect(() => { | |||
| // Dispatch custom event with completion status | |||
| const event = new CustomEvent('pickOrderCompletionStatus', { | |||
| detail: { allLotsCompleted } | |||
| detail: { | |||
| allLotsCompleted, | |||
| tabIndex: 1 // ✅ 明确指定这是来自标签页 1 的事件 | |||
| } | |||
| }); | |||
| window.dispatchEvent(event); | |||
| }, [allLotsCompleted]); | |||
| @@ -41,7 +41,7 @@ const NavigationContent: React.FC = () => { | |||
| }, | |||
| { | |||
| icon: <RequestQuote />, | |||
| label: "Raw Material", | |||
| label: "Store Management", | |||
| path: "", | |||
| children: [ | |||
| { | |||
| @@ -16,7 +16,8 @@ | |||
| "Add Record": "新增", | |||
| "Clean Record": "重置", | |||
| "Dashboard": "資訊展示面板", | |||
| "Raw Material": "原材料", | |||
| "Store Management": "原材料", | |||
| "Store Management": "倉庫管理", | |||
| "Delivery": "交貨單", | |||
| "Scheduling": "排程", | |||
| "Settings": "設定", | |||
| @@ -20,7 +20,7 @@ | |||
| "Reason": "上報原因", | |||
| "escalated date": "上報日期", | |||
| "Order completion": "訂單完成度", | |||
| "Raw material": "原料", | |||
| "Store Management": "原料", | |||
| "Consumable": "消耗品", | |||
| "Shipment": "出貨", | |||
| "Extracted order": "已提取提料單", | |||
| @@ -282,7 +282,12 @@ | |||
| "Delivery No.":"送貨單編號", | |||
| "Total":"總數", | |||
| "completed DO pick orders":"已完成送貨單提料單", | |||
| "No completed DO pick orders found":"沒有已完成送貨單提料單" | |||
| "No completed DO pick orders found":"沒有已完成送貨單提料單", | |||
| "Delivery No":"送貨單編號", | |||
| "View Details":"查看詳情", | |||
| "COMPLETED":"已完成", | |||
| "FG orders":"成品提料單", | |||
| "Back to List":"返回列表" | |||