FPSMS-frontend
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

148 行
5.0 KiB

  1. "use client";
  2. import React, { useState, useRef, useEffect } from 'react';
  3. import { Operator } from "@/app/api/jo";
  4. import { Button, TextField, Typography, Paper, Box, IconButton, Stack } from '@mui/material';
  5. import CloseIcon from '@mui/icons-material/Close';
  6. import { isOperatorExist } from '@/app/api/jo/actions';
  7. import { OperatorQrCode } from './types';
  8. interface OperatorScannerProps {
  9. operators: Operator[];
  10. onOperatorsChange: (operators: Operator[]) => void;
  11. error?: string;
  12. }
  13. const OperatorScanner: React.FC<OperatorScannerProps> = ({
  14. operators,
  15. onOperatorsChange,
  16. error
  17. }) => {
  18. const [scanningMode, setScanningMode] = useState<boolean>(false);
  19. const [scanError, setScanError] = useState<string | null>(null);
  20. const operatorScanRef = useRef<HTMLInputElement>(null);
  21. const startScanning = (): void => {
  22. setScanningMode(true);
  23. setScanError(null);
  24. setTimeout(() => {
  25. if (operatorScanRef.current) {
  26. operatorScanRef.current.focus();
  27. }
  28. }, 100);
  29. };
  30. const stopScanning = (): void => {
  31. setScanningMode(false);
  32. setScanError(null);
  33. };
  34. const handleOperatorScan = async (e: React.KeyboardEvent<HTMLInputElement>): Promise<void> => {
  35. const target = e.target as HTMLInputElement;
  36. const usernameJSON: string = target.value.trim();
  37. if (e.key === 'Enter' || usernameJSON.endsWith('}') ) {
  38. console.log(usernameJSON)
  39. try {
  40. const usernameObj: OperatorQrCode = JSON.parse(usernameJSON)
  41. const response = await isOperatorExist(usernameObj.username)
  42. if (response.message === "Success") {
  43. const isAlreadyAdded = operators.some(op => op.username === response.entity.username);
  44. if (!isAlreadyAdded) {
  45. onOperatorsChange([...operators, response.entity]);
  46. }
  47. target.value = '';
  48. setScanError(null);
  49. // stopScanning();
  50. } else {
  51. setScanError('Operator not found. Please check the ID and try again.');
  52. target.value = '';
  53. }
  54. } catch (error) {
  55. console.error('Error checking operator:', error);
  56. setScanError('An error occurred while checking the operator. Please try again.');
  57. target.value = '';
  58. }
  59. }
  60. };
  61. const removeOperator = (operatorId: number): void => {
  62. onOperatorsChange(operators.filter(op => op.id !== operatorId));
  63. };
  64. return (
  65. <Box>
  66. <Stack direction="row" alignItems="center" justifyContent="space-between" mb={2}>
  67. <Typography variant="h6" fontWeight={600}>
  68. Operators *
  69. </Typography>
  70. <Button
  71. variant={scanningMode ? 'contained' : 'outlined'}
  72. color={scanningMode ? 'success' : 'primary'}
  73. onClick={startScanning}
  74. >
  75. {scanningMode ? 'Scanning...' : 'Scan ID Card'}
  76. </Button>
  77. </Stack>
  78. {scanningMode && (
  79. <Paper elevation={2} sx={{ mb: 2, p: 2, bgcolor: 'green.50', border: '1px solid', borderColor: 'green.200' }}>
  80. <Stack direction="row" alignItems="center" spacing={2}>
  81. <TextField
  82. inputRef={operatorScanRef}
  83. type="text"
  84. onKeyDown={handleOperatorScan}
  85. fullWidth
  86. label="Scan operator ID card or enter manually..."
  87. variant="outlined"
  88. size="small"
  89. sx={{ bgcolor: 'white' }}
  90. onInput={handleOperatorScan}
  91. />
  92. <Button
  93. variant="contained"
  94. color="inherit"
  95. onClick={stopScanning}
  96. >
  97. Cancel
  98. </Button>
  99. </Stack>
  100. {scanError ? (
  101. <Typography variant="body2" color="error" mt={1}>{scanError}</Typography>
  102. ) : (
  103. <Typography variant="body2" color="success.main" mt={1}>
  104. Position the ID card scanner and scan, or type the employee ID manually
  105. </Typography>
  106. )}
  107. </Paper>
  108. )}
  109. {error && <Typography color="error" variant="body2" mb={1}>{error}</Typography>}
  110. <Stack spacing={1}>
  111. {operators.map((operator) => (
  112. <Paper key={operator.id} sx={{ p: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between', bgcolor: 'blue.50', border: '1px solid', borderColor: 'blue.200' }}>
  113. <Box>
  114. <Typography fontWeight={500} color="primary.dark">{operator.name}</Typography>
  115. <Typography variant="body2" color="primary.main">{operator.username}</Typography>
  116. </Box>
  117. <IconButton onClick={() => removeOperator(operator.id)} color="error">
  118. <CloseIcon />
  119. </IconButton>
  120. </Paper>
  121. ))}
  122. {operators.length === 0 && (
  123. <Paper sx={{ p: 2, bgcolor: 'grey.100', border: '1px solid', borderColor: 'grey.200' }}>
  124. <Typography color="text.secondary" fontStyle="italic" variant="body2">
  125. No operators added yet. Use the scan button to add operators.
  126. </Typography>
  127. </Paper>
  128. )}
  129. </Stack>
  130. </Box>
  131. );
  132. };
  133. export default OperatorScanner;