diff --git a/src/app/api/stockTake/actions.ts b/src/app/api/stockTake/actions.ts index 0151196..64154e0 100644 --- a/src/app/api/stockTake/actions.ts +++ b/src/app/api/stockTake/actions.ts @@ -199,6 +199,12 @@ export const getApproverStockTakeRecords = async () => { ); return stockTakeRecords; } +export const getLatestApproverStockTakeHeader = async () => { + return serverFetchJson( + `${BASE_API_URL}/stockTakeRecord/LatestApproverStockTakeHeader`, + { method: "GET" } + ); +} export const createStockTakeForSections = async () => { const createStockTakeForSections = await serverFetchJson>( `${BASE_API_URL}/stockTake/createForSections`, diff --git a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx index 5d20138..861d152 100644 --- a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx +++ b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx @@ -64,7 +64,7 @@ const ApproverStockTakeAll: React.FC = ({ const [batchSaving, setBatchSaving] = useState(false); const [updatingStatus, setUpdatingStatus] = useState(false); const [page, setPage] = useState(0); - const [pageSize, setPageSize] = useState("all"); + const [pageSize, setPageSize] = useState(50); const [total, setTotal] = useState(0); const currentUserId = session?.id ? parseInt(session.id) : undefined; @@ -337,7 +337,19 @@ const ApproverStockTakeAll: React.FC = ({ }, [t, onSnackbar] ); - + const blockNonIntegerKeys = (e: React.KeyboardEvent) => { + // 禁止小数点、逗号、科学计数、正负号 + if ([".", ",", "e", "E", "+", "-"].includes(e.key)) { + e.preventDefault(); + } + }; + + const sanitizeIntegerInput = (value: string) => { + // 只保留数字 + return value.replace(/[^\d]/g, ""); + }; + + const isIntegerString = (value: string) => /^\d+$/.test(value); const handleBatchSubmitAll = useCallback(async () => { if (mode === "approved") return; if (!selectedSession || !currentUserId) { @@ -431,9 +443,16 @@ const ApproverStockTakeAll: React.FC = ({ size="small" type="number" value={variancePercentTolerance} - onChange={(e) => setVariancePercentTolerance(e.target.value)} + onKeyDown={blockNonIntegerKeys} + onChange={(e) => { + const clean = sanitizeIntegerInput(e.target.value); + setVariancePercentTolerance(clean); + + }} + label={t("Variance %")} sx={{ width: 100 }} + inputProps={{ min: 0, max: 100, step: 0.1 }} /> {mode === "pending" && ( @@ -564,7 +583,9 @@ const ApproverStockTakeAll: React.FC = ({ + setQtySelection({ ...qtySelection, [detail.id]: "first", @@ -592,6 +613,7 @@ const ApproverStockTakeAll: React.FC = ({ setQtySelection({ ...qtySelection, @@ -620,6 +642,7 @@ const ApproverStockTakeAll: React.FC = ({ setQtySelection({ ...qtySelection, @@ -634,12 +657,14 @@ const ApproverStockTakeAll: React.FC = ({ size="small" type="number" value={approverQty[detail.id] || ""} - onChange={(e) => + onKeyDown={blockNonIntegerKeys} + onChange={(e) => { + const clean = sanitizeIntegerInput(e.target.value); setApproverQty({ ...approverQty, - [detail.id]: e.target.value, - }) - } + [detail.id]: clean, + }); + }} sx={{ width: 130, minWidth: 130, @@ -650,18 +675,21 @@ const ApproverStockTakeAll: React.FC = ({ }} placeholder={t("Stock Take Qty")} disabled={selection !== "approver"} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} /> + onKeyDown={blockNonIntegerKeys} + onChange={(e) => { + const clean = sanitizeIntegerInput(e.target.value); setApproverBadQty({ ...approverBadQty, - [detail.id]: e.target.value, - }) - } + [detail.id]: clean, + }); + }} sx={{ width: 130, minWidth: 130, @@ -672,6 +700,7 @@ const ApproverStockTakeAll: React.FC = ({ }} placeholder={t("Bad Qty")} disabled={selection !== "approver"} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} /> ={" "} diff --git a/src/components/StockTakeManagement/PickerReStockTake.tsx b/src/components/StockTakeManagement/PickerReStockTake.tsx index df56333..f8353e1 100644 --- a/src/components/StockTakeManagement/PickerReStockTake.tsx +++ b/src/components/StockTakeManagement/PickerReStockTake.tsx @@ -70,7 +70,17 @@ const PickerReStockTake: React.FC = ({ const handleChangePage = useCallback((event: unknown, newPage: number) => { setPage(newPage); }, []); - + const blockNonIntegerKeys = (e: React.KeyboardEvent) => { + // 禁止小数点、逗号、科学计数、正负号 + if ([".", ",", "e", "E", "+", "-"].includes(e.key)) { + e.preventDefault(); + } + }; + const sanitizeIntegerInput = (value: string) => { + // 只保留数字 + return value.replace(/[^\d]/g, ""); + }; + const isIntegerString = (value: string) => /^\d+$/.test(value); const handleChangeRowsPerPage = useCallback((event: React.ChangeEvent) => { const newSize = parseInt(event.target.value, 10); if (newSize === -1) { @@ -437,9 +447,11 @@ const PickerReStockTake: React.FC = ({ size="small" type="number" value={inputs.firstQty} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, @@ -460,9 +472,11 @@ const PickerReStockTake: React.FC = ({ size="small" type="number" value={inputs.firstBadQty} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, @@ -500,13 +514,15 @@ const PickerReStockTake: React.FC = ({ size="small" type="number" value={inputs.secondQty} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, - [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondQty: val } + [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondQty: clean } })); }} sx={{ @@ -523,13 +539,15 @@ const PickerReStockTake: React.FC = ({ size="small" type="number" value={inputs.secondBadQty} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, - [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondBadQty: val } + [detail.id]: { ...(prev[detail.id] ?? defaultInputs), secondBadQty: clean } })); }} sx={{ @@ -581,10 +599,15 @@ const PickerReStockTake: React.FC = ({ setRecordInputs(prev => ({ + onKeyDown={blockNonIntegerKeys} + inputProps={{ inputMode: "text", pattern: "[0-9]*" }} + onChange={(e) => { + const clean = sanitizeIntegerInput(e.target.value); + setRecordInputs(prev => ({ ...prev, - [detail.id]: { ...(prev[detail.id] ?? defaultInputs), remark: e.target.value } - }))} + [detail.id]: { ...(prev[detail.id] ?? defaultInputs), remark: clean } + })); + }} sx={{ width: 150 }} /> diff --git a/src/components/StockTakeManagement/PickerStockTake.tsx b/src/components/StockTakeManagement/PickerStockTake.tsx index a62323d..4050919 100644 --- a/src/components/StockTakeManagement/PickerStockTake.tsx +++ b/src/components/StockTakeManagement/PickerStockTake.tsx @@ -394,7 +394,19 @@ const PickerStockTake: React.FC = ({ window.removeEventListener("keydown", handleKeyPress); }; }, []); - + const blockNonIntegerKeys = (e: React.KeyboardEvent) => { + // 禁止小数点、逗号、科学计数、正负号 + if ([".", ",", "e", "E", "+", "-"].includes(e.key)) { + e.preventDefault(); + } + }; + + const sanitizeIntegerInput = (value: string) => { + // 只保留数字 + return value.replace(/[^\d]/g, ""); + }; + + const isIntegerString = (value: string) => /^\d+$/.test(value); const isSubmitDisabled = useCallback((detail: InventoryLotDetailResponse): boolean => { if (selectedSession?.status?.toLowerCase() === "completed") { return true; @@ -580,9 +592,11 @@ const PickerStockTake: React.FC = ({ size="small" type="number" value={recordInputs[detail.id]?.firstQty || ""} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], firstQty: val } })); }} @@ -604,9 +618,11 @@ const PickerStockTake: React.FC = ({ size="small" type="number" value={recordInputs[detail.id]?.firstBadQty || ""} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], firstBadQty: val } })); }} @@ -656,9 +672,11 @@ const PickerStockTake: React.FC = ({ size="small" type="number" value={recordInputs[detail.id]?.secondQty || ""} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], secondQty: val } })); }} @@ -676,9 +694,11 @@ const PickerStockTake: React.FC = ({ size="small" type="number" value={recordInputs[detail.id]?.secondBadQty || ""} - inputProps={{ min: 0, step: "any" }} + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} + onKeyDown={blockNonIntegerKeys} onChange={(e) => { - const val = e.target.value; + const clean = sanitizeIntegerInput(e.target.value); + const val = clean; if (val.includes("-")) return; setRecordInputs(prev => ({ ...prev, [detail.id]: { ...prev[detail.id], secondBadQty: val } })); }} @@ -751,13 +771,18 @@ const PickerStockTake: React.FC = ({ setRecordInputs(prev => ({ - ...prev, - [detail.id]: { - ...(prev[detail.id] ?? { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" }), - remark: e.target.value - } - }))} + onKeyDown={blockNonIntegerKeys} + inputProps={{ inputMode: "text", pattern: "[0-9]*" }} + onChange={(e) => { + const clean = sanitizeIntegerInput(e.target.value); + setRecordInputs(prev => ({ + ...prev, + [detail.id]: { + ...(prev[detail.id] ?? { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" }), + remark: clean + } + })); + }} sx={{ width: 150 }} /> diff --git a/src/components/StockTakeManagement/StockTakeTab.tsx b/src/components/StockTakeManagement/StockTakeTab.tsx index 12376bb..fb371f5 100644 --- a/src/components/StockTakeManagement/StockTakeTab.tsx +++ b/src/components/StockTakeManagement/StockTakeTab.tsx @@ -3,7 +3,7 @@ import { Box, Tab, Tabs, Snackbar, Alert, CircularProgress, Typography } from "@mui/material"; import { useState, useCallback, useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { AllPickedStockTakeListReponse, getApproverStockTakeRecords } from "@/app/api/stockTake/actions"; +import { AllPickedStockTakeListReponse, getLatestApproverStockTakeHeader } from "@/app/api/stockTake/actions"; import PickerCardList from "./PickerCardList"; import PickerStockTake from "./PickerStockTake"; import PickerReStockTake from "./PickerReStockTake"; @@ -54,12 +54,11 @@ const StockTakeTab: React.FC = () => { }, []); useEffect(() => { - if (tabValue !== 1) return; + if (tabValue !== 1 && tabValue !== 2) return; setApproverLoading(true); - getApproverStockTakeRecords() - .then((records) => { - const list = Array.isArray(records) ? records : []; - setApproverSession(list[0] ?? null); + getLatestApproverStockTakeHeader() + .then((header) => { + setApproverSession(header ?? null); }) .catch((e) => { console.error(e); @@ -150,11 +149,7 @@ const StockTakeTab: React.FC = () => { )} {tabValue === 2 && ( - {approverLoading ? ( - - - - ) : approverSession ? ( + {approverSession ? (