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

7 месяцев назад
7 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
7 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
3 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
3 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
7 месяцев назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. "use client";
  2. import React, { useState, useRef } from "react";
  3. import { MachineQrCode } from "./types";
  4. import {
  5. Button,
  6. TextField,
  7. Typography,
  8. Paper,
  9. Box,
  10. IconButton,
  11. Stack,
  12. } from "@mui/material";
  13. import CloseIcon from "@mui/icons-material/Close";
  14. import { isCorrectMachineUsed } from "@/app/api/jo/actions";
  15. import { Machine } from "@/app/api/jo";
  16. import { useTranslation } from "react-i18next";
  17. interface MachineScannerProps {
  18. machines: Machine[];
  19. onMachinesChange: (machines: Machine[]) => void;
  20. error?: string;
  21. isActive?: boolean;
  22. onActivate?: () => void;
  23. onDeactivate?: () => void;
  24. }
  25. const machineDatabase: Machine[] = [
  26. { id: 1, name: "CNC Mill #1", code: "CNC001", qrCode: "QR-CNC001" },
  27. { id: 2, name: "Lathe #2", code: "LAT002", qrCode: "QR-LAT002" },
  28. { id: 3, name: "Press #3", code: "PRS003", qrCode: "QR-PRS003" },
  29. { id: 4, name: "Welder #4", code: "WLD004", qrCode: "QR-WLD004" },
  30. { id: 5, name: "Drill Press #5", code: "DRL005", qrCode: "QR-DRL005" },
  31. ];
  32. const MachineScanner: React.FC<MachineScannerProps> = ({
  33. machines,
  34. onMachinesChange,
  35. error,
  36. isActive=false,
  37. onActivate,
  38. onDeactivate,
  39. }) => {
  40. const [scanningMode, setScanningMode] = useState<boolean>(false);
  41. const [scanError, setScanError] = useState<string | null>(null);
  42. const machineScanRef = useRef<HTMLInputElement>(null);
  43. const { t } = useTranslation();
  44. const startScanning = (): void => {
  45. setScanningMode(true);
  46. onActivate?.();
  47. setTimeout(() => {
  48. if (machineScanRef.current) {
  49. machineScanRef.current.focus();
  50. }
  51. }, 100);
  52. };
  53. const stopScanning = (): void => {
  54. setScanningMode(false);
  55. onDeactivate?.();
  56. };
  57. const handleMachineScan = async (
  58. e: React.KeyboardEvent<HTMLInputElement>,
  59. ): Promise<void> => {
  60. const target = e.target as HTMLInputElement;
  61. let scannedInput = target.value.trim();
  62. if (e.key === "Enter" || scannedInput.endsWith("}")) {
  63. console.log("Raw machine input:", scannedInput);
  64. try {
  65. let machineCode: string;
  66. // 尝试解析 JSON
  67. try {
  68. const scannedObj: MachineQrCode = JSON.parse(scannedInput);
  69. machineCode = scannedObj.code;
  70. } catch (jsonError) {
  71. // 如果不是 JSON,尝试从花括号中提取
  72. const match = scannedInput.match(/\{([^}?]+)\??}?/);
  73. if (match && match[1]) {
  74. machineCode = match[1].trim();
  75. console.log("Extracted machine code from braces:", machineCode);
  76. } else {
  77. // 如果没有花括号,直接使用输入值
  78. machineCode = scannedInput.replace(/[{}?]/g, '').trim();
  79. console.log("Using plain machine code:", machineCode);
  80. }
  81. }
  82. if (!machineCode) {
  83. setScanError("Invalid input format");
  84. return;
  85. }
  86. // 首先尝试从 API 获取
  87. const response = await isCorrectMachineUsed(machineCode);
  88. if (response.message === "Success") {
  89. const isAlreadyAdded = machines.some(
  90. (m) => m.code === response.entity.code,
  91. );
  92. if (!isAlreadyAdded) {
  93. onMachinesChange([...machines, response.entity]);
  94. }
  95. target.value = "";
  96. setScanError(null);
  97. } else {
  98. // 如果 API 失败,尝试从本地默认数据查找
  99. const localMachine = machineDatabase.find(
  100. (m) => m.code.toLowerCase() === machineCode.toLowerCase()
  101. );
  102. if (localMachine) {
  103. const isAlreadyAdded = machines.some(
  104. (m) => m.code === localMachine.code
  105. );
  106. if (!isAlreadyAdded) {
  107. onMachinesChange([...machines, localMachine]);
  108. }
  109. target.value = "";
  110. setScanError(null);
  111. console.log(" Used local machine data:", localMachine);
  112. } else {
  113. setScanError(
  114. "Machine not found. Please check the code and try again."
  115. );
  116. target.value = "";
  117. }
  118. }
  119. } catch (error) {
  120. console.error("Error processing machine scan:", error);
  121. setScanError(
  122. "An error occurred while checking the machine. Please try again."
  123. );
  124. target.value = "";
  125. }
  126. }
  127. };
  128. const removeMachine = (machineId: number): void => {
  129. onMachinesChange(machines.filter((m) => m.id !== machineId));
  130. };
  131. return (
  132. <Box>
  133. <Stack
  134. direction="row"
  135. alignItems="center"
  136. justifyContent="space-between"
  137. mb={2}
  138. >
  139. <Typography variant="h6" fontWeight={600}>
  140. Machines *
  141. </Typography>
  142. <Button
  143. variant={scanningMode ? "contained" : "outlined"}
  144. color={scanningMode ? "success" : "primary"}
  145. onClick={startScanning}
  146. >
  147. {scanningMode ? "Scanning..." : "Scan QR Code"}
  148. </Button>
  149. </Stack>
  150. {scanningMode && (
  151. <Paper
  152. elevation={2}
  153. sx={{
  154. mb: 2,
  155. p: 2,
  156. bgcolor: "green.50",
  157. border: "1px solid",
  158. borderColor: "green.200",
  159. }}
  160. >
  161. <Stack direction="row" alignItems="center" spacing={2}>
  162. <TextField
  163. inputRef={machineScanRef}
  164. type="text"
  165. onKeyDown={handleMachineScan}
  166. fullWidth
  167. label="Scan machine QR code or enter manually..."
  168. variant="outlined"
  169. size="small"
  170. sx={{ bgcolor: "white" }}
  171. />
  172. <Button variant="contained" color="inherit" onClick={stopScanning}>
  173. Cancel
  174. </Button>
  175. </Stack>
  176. {scanError ? (
  177. <Typography variant="body2" color="error" mt={1}>
  178. {scanError}
  179. </Typography>
  180. ) : (
  181. <Typography variant="body2" color="success.main" mt={1}>
  182. {t(
  183. "Position the QR code scanner and scan, or type the machine code manually",
  184. )}
  185. </Typography>
  186. )}
  187. </Paper>
  188. )}
  189. {error && (
  190. <Typography color="error" variant="body2" mb={1}>
  191. {error}
  192. </Typography>
  193. )}
  194. <Stack spacing={1}>
  195. {machines.map((machine) => (
  196. <Paper
  197. key={machine.id}
  198. sx={{
  199. p: 2,
  200. display: "flex",
  201. alignItems: "center",
  202. justifyContent: "space-between",
  203. bgcolor: "green.50",
  204. border: "1px solid",
  205. borderColor: "green.200",
  206. }}
  207. >
  208. <Box>
  209. <Typography fontWeight={500} color="success.dark">
  210. {machine.name}
  211. </Typography>
  212. <Typography variant="body2" color="success.main">
  213. {machine.code}
  214. </Typography>
  215. </Box>
  216. <IconButton onClick={() => removeMachine(machine.id)} color="error">
  217. <CloseIcon />
  218. </IconButton>
  219. </Paper>
  220. ))}
  221. {machines.length === 0 && (
  222. <Paper
  223. sx={{
  224. p: 2,
  225. bgcolor: "grey.100",
  226. border: "1px solid",
  227. borderColor: "grey.200",
  228. }}
  229. >
  230. <Typography
  231. color="text.secondary"
  232. fontStyle="italic"
  233. variant="body2"
  234. >
  235. No machines added yet. Use the scan button to add machines.
  236. </Typography>
  237. </Paper>
  238. )}
  239. </Stack>
  240. </Box>
  241. );
  242. };
  243. export default MachineScanner;