업로드!
This commit is contained in:
parent
4d92cc2b61
commit
2bf1c54e7a
@ -10,12 +10,13 @@ import Typography from '@mui/material/Typography';
|
|||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Skeleton from '@mui/material/Skeleton';
|
import Skeleton from '@mui/material/Skeleton';
|
||||||
import { InputAdornment, Stack, Button } from '@mui/material';
|
import { InputAdornment, Stack, Button, Modal, IconButton } from '@mui/material';
|
||||||
import MenuList from '@mui/material/MenuList';
|
import MenuList from '@mui/material/MenuList';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
|
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import NoDataFound from 'src/illustrations/dataNotFound';
|
import NoDataFound from 'src/illustrations/dataNotFound';
|
||||||
@ -28,6 +29,65 @@ import { useCurrencyFormatter } from 'src/hooks/formatCurrency';
|
|||||||
// api
|
// api
|
||||||
import * as api from 'src/services';
|
import * as api from 'src/services';
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const getImageUrl = async (ids) => {
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const responses = await Promise.all(
|
||||||
|
ids.map(async (id) => {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.roboflow.com/picup/picup/images/${id}?api_key=s9OJq0UPljSqkPsJY6xP`,
|
||||||
|
requestOptions
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Error fetching image for ID: ${id}, Status: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json(); // JSON 응답 반환
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return responses; // 모든 이미지 정보를 배열로 반환
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching image URLs:', error);
|
||||||
|
return null; // 오류 발생 시 null 반환
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchImages = async (imageBase64) => {
|
||||||
|
const myHeaders = new Headers();
|
||||||
|
myHeaders.append('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
const raw = JSON.stringify({
|
||||||
|
like_image: 'MV09CPgMSn9uhQ5D2nl5',
|
||||||
|
limit: 3,
|
||||||
|
in_dataset: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
'https://api.roboflow.com/picup/picup/search?api_key=s9OJq0UPljSqkPsJY6xP',
|
||||||
|
requestOptions
|
||||||
|
);
|
||||||
|
const result = await response.json();
|
||||||
|
return result; // 반환 추가
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching images:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Search.propTypes = {
|
Search.propTypes = {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
mobile: PropTypes.bool.isRequired
|
mobile: PropTypes.bool.isRequired
|
||||||
@ -56,6 +116,9 @@ export default function Search({ ...props }) {
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [search, setSearch] = React.useState('');
|
const [search, setSearch] = React.useState('');
|
||||||
|
const [image, setImage] = React.useState(null);
|
||||||
|
const [imagesResultID, setImagesResultID] = React.useState(null);
|
||||||
|
const [roboFlowUrlLists, setRoboFlowUrlLists] = React.useState([]);
|
||||||
|
|
||||||
const { data: filters, isLoading: filtersLoading } = useQuery(['get-search-filters'], () => api.getSearchFilters());
|
const { data: filters, isLoading: filtersLoading } = useQuery(['get-search-filters'], () => api.getSearchFilters());
|
||||||
const { mutate, isLoading } = useMutation('search', api.search, {
|
const { mutate, isLoading } = useMutation('search', api.search, {
|
||||||
@ -108,21 +171,48 @@ export default function Search({ ...props }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 파일 선택 시 호출되는 함수
|
const handleFileChange = async (event) => {
|
||||||
const handleFileChange = (event) => {
|
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
console.log('Selected File:', file);
|
|
||||||
|
|
||||||
// 예: 파일을 서버에 업로드하거나 처리
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onloadend = async () => {
|
||||||
console.log('Image Preview URL:', reader.result);
|
const base64Image = reader.result.split(',')[1]; // base64 변환 후 접두사 제거
|
||||||
|
setImage(reader.result);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const objectDetect = async (image) => {
|
||||||
|
try {
|
||||||
|
const result = await searchImages(image);
|
||||||
|
|
||||||
|
// id 배열 추출
|
||||||
|
const imageIds = result.results.map((r) => r.id);
|
||||||
|
|
||||||
|
// 배열을 한 번에 저장
|
||||||
|
setImagesResultID(imageIds);
|
||||||
|
|
||||||
|
console.log('Image IDs:', imageIds); // 디버깅 출력
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in image search:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (imagesResultID && imagesResultID.length > 0) {
|
||||||
|
getImageUrl(imagesResultID).then((url) => {
|
||||||
|
setRoboFlowUrlLists(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [imagesResultID]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (roboFlowUrlLists) {
|
||||||
|
console.log(roboFlowUrlLists);
|
||||||
|
}
|
||||||
|
}, [roboFlowUrlLists]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
@ -167,7 +257,9 @@ export default function Search({ ...props }) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button variant='contained' sx={{ width: '100%', borderRadius: 0 }} onClick={handleImageSearch}>이미지 검색</Button>
|
<Button variant="contained" sx={{ width: '100%', borderRadius: 0 }} onClick={handleImageSearch}>
|
||||||
|
이미지 검색
|
||||||
|
</Button>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
@ -332,13 +424,13 @@ export default function Search({ ...props }) {
|
|||||||
{isLoading ? <Skeleton variant="text" width="200px" /> : product.category}
|
{isLoading ? <Skeleton variant="text" width="200px" /> : product.category}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<Typography variant="subtitle1" color="text.primary" noWrap>
|
{/* <Typography variant="subtitle1" color="text.primary" noWrap>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton variant="text" width="100px" />
|
<Skeleton variant="text" width="100px" />
|
||||||
) : (
|
) : (
|
||||||
fCurrency(cCurrency(product.priceSale))
|
fCurrency(cCurrency(product.priceSale))
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -347,6 +439,63 @@ export default function Search({ ...props }) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>{' '}
|
</Box>{' '}
|
||||||
|
{roboFlowUrlLists &&
|
||||||
|
roboFlowUrlLists.map((i, index) => (
|
||||||
|
<MenuList
|
||||||
|
key={`search-by-images-${i.image.annotation.key}-${index.toString()}`}
|
||||||
|
sx={{
|
||||||
|
pt: 0,
|
||||||
|
mt: 1,
|
||||||
|
overflow: 'auto',
|
||||||
|
px: 1,
|
||||||
|
li: {
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: `1px solid transparent`,
|
||||||
|
'&:hover, &.Mui-focusVisible, &.Mui-selected ': {
|
||||||
|
border: (theme) => `1px solid ${theme.palette.primary.main}`,
|
||||||
|
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.16),
|
||||||
|
h6: {
|
||||||
|
color: 'primary.main'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.active': {
|
||||||
|
border: (theme) => `1px solid ${theme.palette.primary.main}`,
|
||||||
|
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.16),
|
||||||
|
h6: {
|
||||||
|
color: 'primary.main'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
autoFocusItem={!focus}
|
||||||
|
>
|
||||||
|
<MenuItem>
|
||||||
|
<ListItemIcon>
|
||||||
|
<img
|
||||||
|
alt="이미지"
|
||||||
|
src={i.image.urls.original}
|
||||||
|
style={{ width: '40px', height: '40px', borderRadius: '100%' }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
<Stack direction="row" gap={1} alignItems={'center'} justifyContent={'space-between'}>
|
||||||
|
<div>
|
||||||
|
<Typography variant="subtitle1" color="text.primary" noWrap>
|
||||||
|
Object Data Set {index}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary" noWrap>
|
||||||
|
{i.image.tags.map((i) => {
|
||||||
|
return i;
|
||||||
|
})}
|
||||||
|
{` `}
|
||||||
|
<span>{i.image.r * 100}%</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
))}
|
||||||
{multiSelect && (
|
{multiSelect && (
|
||||||
<Stack gap={1} direction={'row'} p={1} justifyContent={'end'}>
|
<Stack gap={1} direction={'row'} p={1} justifyContent={'end'}>
|
||||||
<Button variant="outlined" color="primary" onClick={() => handleSave(selectedProducts)}>
|
<Button variant="outlined" color="primary" onClick={() => handleSave(selectedProducts)}>
|
||||||
@ -358,6 +507,42 @@ export default function Search({ ...props }) {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* 이미지 미리보기 렌더링 */}
|
||||||
|
{image && (
|
||||||
|
<Modal
|
||||||
|
open={image}
|
||||||
|
sx={{ height: '100vh', width: '100vw', display: 'flex', justifyContent: 'center', alignItems: 'center' }}
|
||||||
|
>
|
||||||
|
<Box sx={{ position: 'relative' }}>
|
||||||
|
<Box mt={2} display="flex" justifyContent="center">
|
||||||
|
<IconButton
|
||||||
|
sx={{ position: 'absolute;', right: 0, top: 10, mixBlendMode: 'difference' }}
|
||||||
|
onClick={() => {
|
||||||
|
setImage(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
<img
|
||||||
|
src={image}
|
||||||
|
style={{
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '300px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '1px solid #ddd'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
sx={{ position: 'absolute', bottom: 0, left: '50%', transform: 'translate(-50%, 0px)' }}
|
||||||
|
onClick={() => objectDetect(image)}
|
||||||
|
>
|
||||||
|
이미지 상품 검색
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ import { varFadeInRight } from '../animate';
|
|||||||
// react dropzone
|
// react dropzone
|
||||||
import { useDropzone } from 'react-dropzone';
|
import { useDropzone } from 'react-dropzone';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const DropZoneStyle = styled('div')(({ theme }) => ({
|
const DropZoneStyle = styled('div')(({ theme }) => ({
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
Loading…
Reference in New Issue
Block a user