Parcourir la source

update

master
CANCERYS\kw093 il y a 1 jour
Parent
révision
317f46ba29
9 fichiers modifiés avec 163 ajouts et 77 suppressions
  1. +4
    -2
      src/app/api/jo/actions.ts
  2. +53
    -2
      src/components/ProductionProcess/FinishedQcJobOrderList.tsx
  3. +14
    -16
      src/components/ProductionProcess/ProductionProcessDetail.tsx
  4. +45
    -17
      src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
  5. +2
    -2
      src/components/ProductionProcess/ProductionProcessList.tsx
  6. +22
    -22
      src/components/ProductionProcess/ProductionProcessStepExecution.tsx
  7. +2
    -2
      src/components/Qc/QcStockInModal.tsx
  8. +13
    -4
      src/i18n/zh/common.json
  9. +8
    -10
      src/i18n/zh/jo.json

+ 4
- 2
src/app/api/jo/actions.ts Voir le fichier

@@ -324,9 +324,11 @@ export interface AllJoborderProductProcessInfoResponse {
assignedTo: number;
pickOrderId: number;
pickOrderStatus: string;
itemCode: string;
itemName: string;
requiredQty: number;
jobOrderId: number;
uom: string;
stockInLineId: number;
jobOrderCode: string;
productProcessLineCount: number;
@@ -908,9 +910,9 @@ export const fetchCompletedJobOrderPickOrdersrecords = cache(async () => {
},
);
});
export const fetchJoForPrintQrCode = cache(async () => {
export const fetchJoForPrintQrCode = cache(async (date: string) => {
return serverFetchJson<JobOrderListForPrintQrCodeResponse[]>(
`${BASE_API_URL}/jo/joForPrintQrCode`,
`${BASE_API_URL}/jo/joForPrintQrCode/${date}`,
{
method: "GET",
next: { tags: ["jo-print-qr-code"] },


+ 53
- 2
src/components/ProductionProcess/FinishedQcJobOrderList.tsx Voir le fichier

@@ -17,6 +17,10 @@ import {
Paper,
IconButton,
Tooltip,
FormControl,
InputLabel,
Select,
MenuItem,
} from "@mui/material";
import QrCodeIcon from '@mui/icons-material/QrCode';
import { useTranslation } from "react-i18next";
@@ -50,11 +54,28 @@ const FinishedQcJobOrderList: React.FC<FinishedQcJobOrderListProps> = ({
const [page, setPage] = useState(0);
const [isPrinting, setIsPrinting] = useState(false);
const [printingId, setPrintingId] = useState<number | null>(null);
const [selectedDate, setSelectedDate] = useState<string>("today");
const getDateLabel = (offset: number) => {
return dayjs().subtract(offset, 'day').format('YYYY-MM-DD');
};

// 根据选择的日期获取实际日期字符串
const getDateParam = (dateOption: string): string => {
if (dateOption === "today") {
return dayjs().format('YYYY-MM-DD');
} else if (dateOption === "yesterday") {
return dayjs().subtract(1, 'day').format('YYYY-MM-DD');
} else if (dateOption === "dayBeforeYesterday") {
return dayjs().subtract(2, 'day').format('YYYY-MM-DD');
}
return dayjs().format('YYYY-MM-DD');
};

const fetchJobOrders = useCallback(async () => {
setLoading(true);
try {
const data = await fetchJoForPrintQrCode();
const dateParam = getDateParam(selectedDate);
const data = await fetchJoForPrintQrCode(dateParam);
setJobOrders(data || []);
setPage(0);
} catch (e) {
@@ -63,7 +84,9 @@ const FinishedQcJobOrderList: React.FC<FinishedQcJobOrderListProps> = ({
} finally {
setLoading(false);
}
}, []);
}, [selectedDate]);



useEffect(() => {
fetchJobOrders();
@@ -114,6 +137,34 @@ const FinishedQcJobOrderList: React.FC<FinishedQcJobOrderListProps> = ({

return (
<Box>
{/* Date Selector */}
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: 'flex-start' }}>
<Box sx={{ maxWidth: 300 }}>
<FormControl fullWidth size="small">
<InputLabel id="date-select-label">{t("Select Date")}</InputLabel>
<Select
labelId="date-select-label"
id="date-select"
value={selectedDate}
// label={t("Select Date")}
onChange={(e) => {
setSelectedDate(e.target.value);
}}
>
<MenuItem value="today">
{t("Today")} ({getDateLabel(0)})
</MenuItem>
<MenuItem value="yesterday">
{t("Yesterday")} ({getDateLabel(1)})
</MenuItem>
<MenuItem value="dayBeforeYesterday">
{t("Day Before Yesterday")} ({getDateLabel(2)})
</MenuItem>
</Select>
</FormControl>
</Box>
</Stack>

{loading ? (
<Box sx={{ display: "flex", justifyContent: "center", p: 3 }}>
<CircularProgress />


+ 14
- 16
src/components/ProductionProcess/ProductionProcessDetail.tsx Voir le fichier

@@ -56,7 +56,7 @@ const ProductionProcessDetail: React.FC<ProductProcessDetailProps> = ({
onBack,
fromJosave,
}) => {
const { t } = useTranslation();
const { t } = useTranslation("common");
const { data: session } = useSession() as { data: SessionWithTokens | null };
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const { values: qrValues, startScan, stopScan, resetScan } = useQrCodeScannerContext();
@@ -334,16 +334,6 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
const effectiveEquipmentCode =
scannedEquipmentCode ?? null;
if (!effectiveEquipmentCode) {
console.error("No equipment code available");
alert(t("Please scan equipment code or equipment detail ID"));
setIsAutoSubmitting(false);
if (autoSubmitTimerRef.current) {
clearTimeout(autoSubmitTimerRef.current);
autoSubmitTimerRef.current = null;
}
return false;
}
console.log("Submitting scan data with equipmentCode:", {
productProcessLineId: lineId,
@@ -353,7 +343,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
const response = await newUpdateProductProcessLineQrscan({
productProcessLineId: lineId,
equipmentCode: effectiveEquipmentCode,
equipmentCode: effectiveEquipmentCode ?? "",
staffNo: scannedStaffNo,
});
@@ -423,6 +413,8 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
setProcessedQrCodes(new Set());
setScannedOperatorId(null);
setScannedEquipmentId(null);
setScannedStaffNo(null); // ✅ Add this
setScannedEquipmentCode(null);
setIsAutoSubmitting(false); // 添加:重置自动提交状态
setLineDetailForScan(null);
// 获取 line detail 以获取 bomProcessEquipmentId
@@ -446,7 +438,9 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
}
setIsManualScanning(false);
setIsAutoSubmitting(false); // 添加:重置自动提交状态
setIsAutoSubmitting(false);
setScannedStaffNo(null); // ✅ Add this
setScannedEquipmentCode(null);
stopScan();
resetScan();
}, [stopScan, resetScan]);
@@ -461,7 +455,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
}
};
// 提交扫描结果并验证
/*
useEffect(() => {
console.log("Auto-submit check:", {
scanningLineId,
@@ -501,6 +495,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
// 只在组件卸载时清除
};
}, [scanningLineId, scannedStaffNo, scannedEquipmentCode, isAutoSubmitting, isManualScanning, submitScanAndStart]);
*/
useEffect(() => {
return () => {
if (autoSubmitTimerRef.current) {
@@ -518,6 +513,9 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
setScannedEquipmentId(null);
setProcessedQrCodes(new Set());
setScannedStaffNo(null);
setScannedEquipmentCode(null);
setProcessedQrCodes(new Set());
// 清除之前的定时器
if (autoSubmitTimerRef.current) {
clearTimeout(autoSubmitTimerRef.current);
@@ -783,7 +781,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
{/* ✅ Show both options */}
{scannedEquipmentCode
? `${t("Equipment Code")}: ${scannedEquipmentCode}`
: t("Please scan equipment code or equipment detail id")
: t("Please scan equipment code")
}
</Typography>
</Box>
@@ -809,7 +807,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => {
<Button
variant="contained"
onClick={() => scanningLineId && handleSubmitScanAndStart(scanningLineId)}
disabled={!scannedStaffNo || (!scannedEquipmentCode)}
disabled={!scannedStaffNo }
>
{t("Submit & Start")}
</Button>


+ 45
- 17
src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx Voir le fichier

@@ -316,7 +316,7 @@ const handleRelease = useCallback(async ( jobOrderId: number) => {
headerAlign: "left",
type: "number",
renderCell: (params) => {
return <Typography sx={{ fontSize: "18px" }}>{params.value}</Typography>;
return <Typography sx={{ fontWeight: 500 }}>{params.value}</Typography>;
},
},
{
@@ -326,16 +326,28 @@ const handleRelease = useCallback(async ( jobOrderId: number) => {
align: "left",
headerAlign: "left",
renderCell: (params) => {
return <Typography sx={{ fontSize: "18px" }}>{params.value || ""}</Typography>;
return(
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
<Typography sx={{ fontWeight: 500 }}>&nbsp;</Typography>
<Typography sx={{ fontWeight: 500 }}>{params.value || ""}</Typography>
<Typography sx={{ fontWeight: 500 }}>&nbsp;</Typography>
</Box>
)
},
},

];
const productionProcessesLineRemarkTableRows =
processData?.productProcessLines?.map((line: any) => ({
id: line.seqNo,
seqNo: line.seqNo,

description: line.description ?? "",



})) ?? [];


@@ -487,21 +499,37 @@ const handleRelease = useCallback(async ( jobOrderId: number) => {
/>
</Box>
);
const ProductionProcessesLineRemarkTableContent = () => (
<Box sx={{ mt: 2 }}>
<ProcessSummaryHeader processData={processData} />
<StyledDataGrid
sx={{
"--DataGrid-overlayHeight": "100px",
}}
disableColumnMenu
rows={productionProcessesLineRemarkTableRows ?? []}
columns={productionProcessesLineRemarkTableColumns}
getRowHeight={() => 'auto'}
/>
</Box>
);
const ProductionProcessesLineRemarkTableContent = () => (
<Box sx={{ mt: 2 }}>
<ProcessSummaryHeader processData={processData} />
<StyledDataGrid
sx={{
"--DataGrid-overlayHeight": "100px",
// ✅ Match ProductionProcessDetail font size (default body2 = 0.875rem)
"& .MuiDataGrid-cell": {
fontSize: "0.875rem", // ✅ Match default body2 size
fontWeight: 500,
},
"& .MuiDataGrid-columnHeader": {
fontSize: "0.875rem", // ✅ Match header size
fontWeight: 600,
},
// ✅ Ensure empty columns are visible
"& .MuiDataGrid-columnHeaders": {
display: "flex",
},
"& .MuiDataGrid-row": {
display: "flex",
},
}}
disableColumnMenu
rows={productionProcessesLineRemarkTableRows ?? []}
columns={productionProcessesLineRemarkTableColumns}
getRowHeight={() => 'auto'}
hideFooter={false} // ✅ Ensure footer is visible
/>
</Box>
);

return (


+ 2
- 2
src/components/ProductionProcess/ProductionProcessList.tsx Voir le fichier

@@ -235,10 +235,10 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess
</Stack>

<Typography variant="body2" color="text.secondary">
{t("Item Name")}: {process.itemName}
{t("Item Name")}: {process.itemCode} {process.itemName}
</Typography>
<Typography variant="body2" color="text.secondary">
{t("Required Qty")}: {process.requiredQty}
{t("Required Qty")}: {process.requiredQty} {process.uom}
</Typography>
<Typography variant="body2" color="text.secondary">
{t("Production date")}: {process.date ? dayjs(process.date as any).format(OUTPUT_DATE_FORMAT) : "-"}


+ 22
- 22
src/components/ProductionProcess/ProductionProcessStepExecution.tsx Voir le fichier

@@ -410,7 +410,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
<Card sx={{ bgcolor: 'success.50', border: '2px solid', borderColor: 'success.main', mb: 3 }}>
<CardContent>
<Typography variant="h5" color="success.main" gutterBottom fontWeight="bold">
{t("Completed Step")}: {lineDetail?.name} (Seq: {lineDetail?.seqNo})
{t("Completed Step")}: {lineDetail?.name} ({t("Seq")}: {lineDetail?.seqNo})
</Typography>
{/*<Divider sx={{ my: 2 }} />*/}
@@ -420,27 +420,27 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
{t("Step Information")}
</Typography>
<Grid container spacing={2} sx={{ mb: 3 }}>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary">
<strong>{t("Description")}:</strong> {lineDetail?.description || "-"}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary">
<strong>{t("Operator")}:</strong> {lineDetail?.operatorName || "-"}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary">
<strong>{t("Equipment")}:</strong> {equipmentName}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary">
<strong>{t("Status")}:</strong> {lineDetail?.status || "-"}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '1.25rem' }}>
<strong>{t("Description")}:</strong> {lineDetail?.description || "-"}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '1.25rem' }}>
<strong>{t("Operator")}:</strong> {lineDetail?.operatorName || "-"}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '1.25rem' }}>
<strong>{t("Equipment")}:</strong> {equipmentName}
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '1.25rem' }}>
<strong>{t("Status")}:</strong> {t(lineDetail?.status || "-")}
</Typography>
</Grid>
</Grid>

{/*<Divider sx={{ my: 2 }} />*/}

@@ -563,7 +563,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
<Card sx={{ bgcolor: 'primary.50', border: '2px solid', borderColor: 'primary.main', height: '100%' }}>
<CardContent>
<Typography variant="h6" color="primary.main" gutterBottom>
{t("Executing")}: {lineDetail?.name} (Seq: {lineDetail?.seqNo})
{t("Executing")}: {lineDetail?.name} ({t("Seq")}:{lineDetail?.seqNo})
</Typography>
<Typography variant="body2" color="text.secondary">
{lineDetail?.description}


+ 2
- 2
src/components/Qc/QcStockInModal.tsx Voir le fichier

@@ -397,7 +397,7 @@ const QcStockInModal: React.FC<Props> = ({
// confirmButtonText: t("confirm putaway"), html: ""});
// onOpenPutaway();
const isJobOrderBom = (stockInLineInfo?.jobOrderId != null || printSource === "productionProcess")
&& stockInLineInfo?.bomDescription === "半成品";
&& stockInLineInfo?.bomDescription === "WIP";
if (isJobOrderBom) {
// Auto putaway to default warehouse
const defaultWarehouseId = stockInLineInfo?.defaultWarehouseId ?? 1;
@@ -417,7 +417,7 @@ const QcStockInModal: React.FC<Props> = ({
itemId: stockInLineInfo?.itemId, // Include Item ID
purchaseOrderId: stockInLineInfo?.purchaseOrderId, // Include PO ID if exists
purchaseOrderLineId: stockInLineInfo?.purchaseOrderLineId, // Include POL ID if exists
acceptedQty: stockInLineInfo?.acceptedQty, // Include acceptedQty
acceptedQty:acceptQty, // Include acceptedQty
acceptQty: stockInLineInfo?.acceptedQty, // Putaway quantity
warehouseId: defaultWarehouseId,
status: "received", // Use string like PutAwayModal


+ 13
- 4
src/i18n/zh/common.json Voir le fichier

@@ -84,7 +84,14 @@
"Detail Scheduling": "詳細排程",
"Customer": "客戶",
"qcItem": "品檢項目",
"Item": "物料",
"Item": "成品/半成品",
"Today": "今天",
"Yesterday": "昨天",
"Input Equipment is not match with process": "輸入的設備與流程不匹配",
"Staff No is required": "員工編號必填",
"Day Before Yesterday": "前天",
"Select Date": "選擇日期",
"Production Date": "生產日期",
"QC Check Item": "QC品檢項目",
"QC Category": "QC品檢模板",
@@ -179,7 +186,6 @@
"Production Process Information": "生產流程信息",
"Production Process Steps": "生產流程步驟",
"Scan Operator & Equipment": "掃描操作員和設備",
"Seq": "序號",
"Setup Time (mins)": "生產前預備時間(分鐘)",
"Start": "開始",
"Start QR Scan": "開始掃碼",
@@ -195,10 +201,13 @@
"Validation failed. Please check operator and equipment.": "驗證失敗. 請檢查操作員和設備.",
"View": "查看",
"Back": "返回",
"BoM Material": "物料清單",
"BoM Material": "成品/半成品清單",
"N/A": "不適用",
"Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間次序 | 複雜度",
"Item Code": "物料編號",
"Item Code": "成品/半成品名稱",
"Please scan equipment code": "請掃描設備編號",
"Equipment Code": "設備編號",
"Seq": "步驟",
"Item Name": "物料名稱",
"Job Order Info": "工單信息",
"Matching Stock": "工單對料",


+ 8
- 10
src/i18n/zh/jo.json Voir le fichier

@@ -8,7 +8,7 @@
"Code": "工單編號",
"Name": "成品/半成品名稱",
"Picked Qty": "已提料數量",
"Confirm All": "確認所有",
"Confirm All": "確認所有提料",
"UoM": "銷售單位",
"No": "沒有",
"User not found with staffNo:": "用戶不存在",
@@ -156,7 +156,9 @@
"Reject": "拒絕",
"Stock Unit": "庫存單位",
"Group": "組",
"Item": "物料",
"Input Equipment is not match with process": "輸入的設備與流程不匹配",
"Item": "成品/半成品",
"Select Date": "選擇日期",
"No Group": "沒有組",
"No created items": "沒有創建物料",
"Order Quantity": "需求數量",
@@ -284,7 +286,6 @@
"acceptQty must not greater than": "接受數量不能大於",
"escalation": "升級",
"failedQty": "失敗數量",
"qcItem": "QC物料",
"qcResult": "QC結果",
"remarks": "備註",
"supervisor": "主管",
@@ -334,13 +335,15 @@
"pending": "待處理",

"Please scan equipment code (optional if not required)": "請掃描設備編號(可選)",
"Please scan equipment code": "請掃描設備編號",
"Equipment Code": "設備編號",
"Please scan operator code": "請掃描操作員編號",
"Please scan operator code first": "請先掃描操作員編號",
"Processing Time (mins)": "步驟時間(分鐘)",
"Production Process Information": "生產流程信息",
"Production Process Steps": "生產流程步驟",
"Scan Operator & Equipment": "掃描操作員和設備",
"Seq": "序號",
"Seq:": "步驟",
"Setup Time (mins)": "生產前預備時間(分鐘)",
"Start": "開始",
"Start QR Scan": "開始掃碼",
@@ -366,18 +369,13 @@
"View": "查看",
"Back": "返回",
"N/A": "不適用",
"BoM Material": "物料清單",
"BoM Material": "成品/半成品清單",
"Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間順序 | 複雜度",
"Item Code": "物料編號",
"Item Name": "物料名稱",
"Enter the number of cartons: ": "請輸入箱數:",
"Number of cartons": "箱數",
"You need to enter a number": "您需要輸入一個數字",
"Number must be at least 1": "數字必須至少為1",

"Cancel": "取消",
"Print Pick Record": "打印板頭紙",
"Printed Successfully.": "成功列印",
"Job Order Info": "工單信息",
"Matching Stock": "工單對料",
"No data found": "沒有找到資料",


Chargement…
Annuler
Enregistrer