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